From b147bc428d1bf6fbcc3d633bf50ce24d6c3f9500 Mon Sep 17 00:00:00 2001 From: Waldheim Date: Sun, 7 Mar 2021 08:49:21 -0500 Subject: [PATCH 01/19] automatic package handling --- arbinimport.R | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/arbinimport.R b/arbinimport.R index c26acf0..ee1c1d0 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -12,21 +12,24 @@ # # ###### -require(readxl) -require(dplyr) -require(shiny) -require(tcltk) -require(DT) -require(shinyjs) -require(shinyalert) -require(pracma) -require(purrr) -require(zoo) -require(plotrix) -require(tools) -require(shinyWidgets) +list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") +new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] +if(length(new.packages)) install.packages(new.packages) -require(gifski) +# require(readxl) +# require(dplyr) +# require(shiny) +# require(tcltk) +# require(DT) +# require(shinyjs) +# require(shinyalert) +# require(pracma) +# require(purrr) +# require(zoo) +# require(plotrix) +# require(tools) +# require(shinyWidgets) +# require(gifski) # This line tests if the current R environment is interactive, RStudio makes an interactive environment by default if (interactive()) { From be5553591f5e53d2924bdaa202597889c4d1deae Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Sun, 7 Mar 2021 10:48:49 -0500 Subject: [PATCH 02/19] history used for masses --- arbinimport.R | 94 ++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/arbinimport.R b/arbinimport.R index ee1c1d0..6f33b2e 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -16,21 +16,6 @@ list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shi new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] if(length(new.packages)) install.packages(new.packages) -# require(readxl) -# require(dplyr) -# require(shiny) -# require(tcltk) -# require(DT) -# require(shinyjs) -# require(shinyalert) -# require(pracma) -# require(purrr) -# require(zoo) -# require(plotrix) -# require(tools) -# require(shinyWidgets) -# require(gifski) - # This line tests if the current R environment is interactive, RStudio makes an interactive environment by default if (interactive()) { @@ -84,14 +69,17 @@ if (interactive()) { fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), + fluidRow(div(style = "color: red; font-size: 20pt;", "!!! Pay Attention. Controls Have Moved !!!"), align = "center"), + # This first column is where most user inputs are, with the exception of the directory name - column(4, + column(4, align = "center", # This generates the optional block in which the user can import a previous R environment fluidRow( - strong("Files to be Analyzed*"), tags$br(), - "Import all Arbin files of interest.", tags$br(), tags$br(), - fileInput("files", NULL, multiple = TRUE), + strong("Start Here"), tags$br(), + "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), + actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width:80%; margin:5%; font-size:100%"), tags$br(), + helpText("The analysis will create a folder within the selected folder."), style = "border: 1px solid black; padding: 5%; margin:5%" ), @@ -136,10 +124,10 @@ if (interactive()) { # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process column(4, align ="center", fluidRow( - textInput("dirName", "Analysis Name*"), tags$br(), - "Current Location: ", textOutput("currDir", inline = TRUE), tags$br(), - actionButton("chooseDir", "Change Output Location*", class = "btn-secondary", style = "width:80%; margin:5%; font-size:100%"), tags$br(), - helpText("The analysis will create a folder within the selected folder."), + selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), + strong("Files to be Analyzed*"), tags$br(), + "Import all Arbin files of interest.", tags$br(), tags$br(), + fileInput("files", NULL, multiple = TRUE), actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width:80%; height:100px; margin:5%; font-size:100%"), style = "border: 4px double black; padding: 5%; margin:5%" ), @@ -310,9 +298,23 @@ if (interactive()) { }) observeEvent(input$chooseDir, { - chosenDir = tk_choose.dir() + chosenDir <- tk_choose.dir() dirLocation(chosenDir) + internal_folders <- list.dirs(path = dirLocation(), recursive = FALSE) + base_names = vector() + for (folder in internal_folders) { + base_names <- append(base_names, split_path(folder)[1]) + } + if ("history" %in% base_names) { + load(paste(dirLocation(), "/history/", "Formation.RData", sep ="")) + + data <<- data + + output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), + colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + } + if (!is.na(dirLocation())) { output$currDir <- renderText({paste(split_path(dirLocation())[1], "(", split_path(dirLocation())[2], ")")}) } @@ -431,25 +433,31 @@ if (interactive()) { # After the validation of the Arbin files, they macros (file name, and sheets) are taken and rendered in to a datatable renderTable <- function() { - output$channels <- renderDataTable({ - files <- input$files - - if (is.null(files)) { - return(NULL) - } - - file_sheet <- data.frame() - for (i in 1:nrow(files)) { - sheets <- excel_sheets(files[i, 4]) - file_sheet <- rbind(file_sheet, data.frame(name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), - datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) - } - - data <<- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) - - data - - }, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), + + files <- input$files + + if (is.null(files)) { + return(NULL) + } + + file_sheet <- data.frame() + for (i in 1:nrow(files)) { + sheets <- excel_sheets(files[i, 4]) + file_sheet <- rbind(file_sheet, data.frame(name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), + datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) + } + + raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) + + if (!is.null(dim(data)[1])) { + new_rows <- intersect(raw_data$sheet, data$sheet) + data <<- data[which(data$sheet %in% new_rows),] + data$datapath <<- raw_data$datapath + } else { + data <<- raw_data + } + + output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) } From 54132af088418232732fcfae3ea40c5893837f0b Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Sun, 7 Mar 2021 11:11:11 -0500 Subject: [PATCH 03/19] removed total file generation --- arbinimport.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arbinimport.R b/arbinimport.R index 6f33b2e..bafa5f6 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -793,7 +793,7 @@ if (interactive()) { # Save all data within the cell's directory write.csv(tmp_excel, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) - + # Append summation data to the larger datasets to be worked with later final <- rbind(final, tmp_excel) numCycles <<- rbind(numCycles, data.frame(sheet=data$sheet[row], cycles=nrow(cell_data))) @@ -842,7 +842,6 @@ if (interactive()) { # Save total data and stats write.csv(stats, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Summary.csv", sep ="")) - write.csv(final, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Total.csv", sep ="")) write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," dQdV Data.csv", sep ="")) write.csv(cycle_facts, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Cycle Facts.csv", sep ="")) From 04b6932cf677ec11d25b26274ffcd42204982e12 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Sun, 7 Mar 2021 11:41:21 -0500 Subject: [PATCH 04/19] added raw caps to cycle_facts --- arbinimport.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbinimport.R b/arbinimport.R index c26acf0..74430ad 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -683,7 +683,7 @@ if (interactive()) { # Record charge and discharge voltage, then calculate the delta and average voltage cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, - dV=chV-dchV, DCap = DCap, CCap = CCap, CE = (CCap / DCap) * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, + dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = (CCap / DCap) * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) From 2872a0442f9d86ec89e486f6b50bc6a68c66ee97 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Sun, 7 Mar 2021 11:58:06 -0500 Subject: [PATCH 05/19] CE calculation fixed for anode/cathode --- arbinimport.R | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arbinimport.R b/arbinimport.R index 74430ad..02c6fbe 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -681,9 +681,15 @@ if (interactive()) { capCVFracCh <- caps[1] / (caps[1] + caps[2]) capCVFracDch <- caps[3] / (caps[3] + caps[4]) + if (max(tmp_excel$voltage) > 2) { + CE <- DCap / CCap + } else { + CE <- CCap / DCap + } + # Record charge and discharge voltage, then calculate the delta and average voltage cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, - dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = (CCap / DCap) * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, + dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = CE * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) From 995bd0860c012b0a838d3c8f3e4d1f842d849057 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Mon, 8 Mar 2021 16:14:01 -0500 Subject: [PATCH 06/19] require packages --- arbinimport.R | 1 + 1 file changed, 1 insertion(+) diff --git a/arbinimport.R b/arbinimport.R index c8aa5bf..abab61a 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -15,6 +15,7 @@ list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] if(length(new.packages)) install.packages(new.packages) +lapply(list.of.packages, require, character.only = TRUE) # This line tests if the current R environment is interactive, RStudio makes an interactive environment by default if (interactive()) { From 25b1aa6c314cc4f80dc77b069f2a95ee780d3e3c Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Tue, 4 May 2021 11:12:02 -0400 Subject: [PATCH 07/19] read CM files --- arbinimport.R | 59 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/arbinimport.R b/arbinimport.R index abab61a..aff5f30 100644 --- a/arbinimport.R +++ b/arbinimport.R @@ -42,6 +42,7 @@ if (interactive()) { xlabel <-"" ylabel <-"" addParams <- FALSE + arbinCM <- FALSE catMetric <<- vector() legTitle <<-"" sheetName <<-"" @@ -165,7 +166,7 @@ if (interactive()) { server <- function(input, output, session) { # This sets the maximum file size Shiny will import, the default of 5Mb is not large enough to handle Arbin files - options(shiny.maxRequestSize=100*1024^2) + options(shiny.maxRequestSize=1000*1024^2) split_path <- function(x) if (dirname(x)==x) x else c(basename(x),split_path(dirname(x))) @@ -286,7 +287,11 @@ if (interactive()) { validFile <- FALSE for (file in input$files) { - if (file_ext(file) =="xlsx" | file_ext(file) =="xls") { + if (file_ext(file) == "xlsx" | file_ext(file) == "xls") { + validFile <- TRUE + arbinCM <<- FALSE + } else if (file_ext(file) == "csv") { + arbinCM <<- TRUE validFile <- TRUE } } @@ -294,7 +299,7 @@ if (interactive()) { if (validFile) { renderTable() } else { - shinyalert("That isn't right...","Please upload an Excel file.","error") + shinyalert("That isn't right...","Please upload an Excel or CSV file.","error") } }) @@ -308,7 +313,7 @@ if (interactive()) { base_names <- append(base_names, split_path(folder)[1]) } if ("history" %in% base_names) { - load(paste(dirLocation(), "/history/", "Formation.RData", sep ="")) + load(paste(dirLocation(), "/history/", "RateCap.RData", sep ="")) data <<- data @@ -442,14 +447,18 @@ if (interactive()) { } file_sheet <- data.frame() - for (i in 1:nrow(files)) { - sheets <- excel_sheets(files[i, 4]) - file_sheet <- rbind(file_sheet, data.frame(name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), - datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) + if (arbinCM) { + raw_data <- data.frame(name = files[["name"]], "sheet" = 1:nrow(files), datapath = files[["datapath"]]) + } else { + for (i in 1:nrow(files)) { + sheets <- excel_sheets(files[i, 4]) + file_sheet <- rbind(file_sheet, data.frame(name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), + datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) + } + + raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) } - raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) - if (!is.null(dim(data)[1])) { new_rows <- intersect(raw_data$sheet, data$sheet) data <<- data[which(data$sheet %in% new_rows),] @@ -458,8 +467,13 @@ if (interactive()) { data <<- raw_data } - output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), - colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + if (arbinCM) { + output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), + colnames = c("File", "Sheet", "Filepath")) + } else { + output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(4)))), + colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + } } observeEvent(input$whatGraph, { @@ -474,7 +488,7 @@ if (interactive()) { } else { tryCatch({ masses <- lapply(strsplit(strRep(input$masses, "\n", ","), ",", fixed = TRUE), as.double) - names(masses)[names(masses) =="V1"] <-"Mass" + names(masses)[names(masses) =="V1"] <- "Mass" data$Mass <<- masses[[1]] }, error = function(cond) { print(cond) @@ -564,7 +578,12 @@ if (interactive()) { # ###### # Import the excel sheet corresponding to cell of interest - tmp_excel <- read_excel(toString(data$datapath[row]), toString(data$sheet[row])) + if (arbinCM) { + tmp_excel <- read.csv(toString(data$datapath[row])) + names(tmp_excel) <- c("Index", "Test_Time(s)", "Date_Time", "Step_Time(s)", "Step_Index", "Cycle_Index", "Current(A)", "Voltage(V)", "Charge_Capacity(Ah)", "Discharge_Capacity(Ah)", "Charge_Energy(Wh)", "Discharge_Energy(Wh)") + } else { + tmp_excel <- read_excel(toString(data$datapath[row]), toString(data$sheet[row])) + } # Create an nested directory for all the data and, if applicable, then further folders for graphs of interest dir.create(paste(dirLocation(), "/", input$dirName, data$sheet[row], sep ="/")) @@ -652,10 +671,10 @@ if (interactive()) { caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] ch_dch <- FALSE if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { - dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) prev_c = step$`Current(A)`[[1]] } else { - dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) } } } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { @@ -677,8 +696,8 @@ if (interactive()) { # # ###### - dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] - dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] + #dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] + #dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] if (sum(data$Mass) != 0) { DCap <- tail(cycle$Q.d, 1) @@ -849,7 +868,7 @@ if (interactive()) { # Save total data and stats write.csv(stats, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Summary.csv", sep ="")) - write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," dQdV Data.csv", sep ="")) + if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," dQdV Data.csv", sep ="")) write.csv(cycle_facts, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Cycle Facts.csv", sep ="")) # If a histor directory does not exist, create it. Save all the data revelant to plotting to a RData file. @@ -1259,7 +1278,7 @@ if (interactive()) { # Error handling for graphBuilder and then showing modal observeEvent(input$graphBuilder, { - if(dim(numCycles)[1] == 0 | dim(dQdVData)[1] == 0 | dim(cycle_facts)[1] == 0 | dim(total) == 0) { + if(dim(numCycles)[1] == 0 | dim(dQdVData)[1] == 0 | dim(cycle_facts)[1] == 0) { shinyalert("No Data!","Please run the analysis first or load a previous environment.","error") } else { updateCheckboxGroupInput(session,"cells", choices = data$sheet) From a928d0714f8f0e51811ae7fa4c07e5e8ec9b4f55 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Sat, 29 May 2021 19:55:51 -0400 Subject: [PATCH 08/19] ui + server broken out --- .Rhistory | 512 ++++++++++++++++++++++++++++++ global.R | 56 ++++ arbinimport.R => server.R | 643 ++++++++++++++------------------------ ui.R | 108 +++++++ 4 files changed, 915 insertions(+), 404 deletions(-) create mode 100644 .Rhistory create mode 100644 global.R rename arbinimport.R => server.R (68%) create mode 100644 ui.R diff --git a/.Rhistory b/.Rhistory new file mode 100644 index 0000000..7bd7632 --- /dev/null +++ b/.Rhistory @@ -0,0 +1,512 @@ +devtools::install_github("eddelbuettel/rbenchmark") +library('devtools') +install.packages('devtools') +devtools::install_github("eddelbuettel/rbenchmark") +library('devtools') +install_github("eddelbuettel/rbenchmark") +install.packages("devtools") +install_github("eddelbuettel/rbenchmark") +install.packages("C:/Users/rwaldhei/Downloads/rbenchmark_1.0.0.tar.gz", repos = NULL, type = "source") +library('rbenchmark') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +install.packages("rlang") +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library('rbenchmark') +install.packages("rlang") +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +sessionIfo() +sessionInfo() +library('rlang') +sessionInfo() +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +install.packages("rlang", version="0.4.6") +library('rbenchmark') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +install.packages("tidyverse") +install.packages("tidyverse") +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library('rbenchmark') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +install.packages("C:/Users/rwaldhei/Downloads/rlang_0.4.10.tar.gz", repos = NULL, type = "source") +uninstall.package('rlang') +uninstall.packages('rlang') +remove.packages('rlang') +install.packages("C:/Users/rwaldhei/Downloads/rlang_0.4.10.tar.gz", repos = NULL, type = "source") +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +install.packages("installr") +library(installr) +updateR() +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library('shinyjs') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +list.dirs() +list.dirs(path = ".", full.names = TRUE, recursive = TRUE) +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +base_names +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +data$data +data +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +data +data$data$sheet +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +data +raw_data +raw_data$sheet +data$sheet +data <<- data[raw_data$sheet %in% data$sheet] +data +data[raw_data$sheet %in% data$sheet] +data[intersect(raw_data$sheet, data$sheet)] +intersect(raw_data$sheet, data$sheet) +new_rows <- intersect(raw_data$sheet, data$sheet) +data[data$sheet %in% new_rows] +data%sheet %in% new_rows +data$sheet %in% new_rows +data[which(data$sheet %in% new_rows),] +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +profvis::profvis({ # Modal for completed analysis +shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation(), "/", input$dirName, sep = ""), +type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", +callbackR = function(x) { +if (x) { +export_to_origin() +} +} +)}) +profvis::profvis({ # Sets up a progress bar in which to estimate how long the execution of the code will take +progress <- Progress$new(session, min = 0, max = nrow(data)) +progress$set(message ="Plugging and chugging...\n", detail ="Starting up...") +# Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) +while (dev.cur() != 1) { +dev.off() +} +# Resets the variables for the graph builder so new results are concatenated to old ones +numCycles <<- data.frame() +dQdVData <<- data.frame() +total <<- data.frame() +cycle_facts <<- data.frame() +# Disable all input fields to prevent errors occurring from changing values +disable("files") +disable("lowV") +disable("highV") +disable("dirLocation()") +disable("submit") +disable("excelImport") +disable("gGraphs") +disable("peakFit") +disable("area") +disable("perActive") +disable("capActive") +# Creates the directory in which all data will be stored +dir.create(paste(dirLocation(), input$dirName, sep = "/")) +# Update the status once all set-up functions are complete +progress$set(detail ="Starting first cell...") +# ###### +# +# The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. +# +# ###### +for (row in 1:nrow(data)) { +# ###### +# +# This is where all code that should be executed on a"per cell" basis, to prepare for analysis +# +# ###### +# Import the excel sheet corresponding to cell of interest +tmp_excel <- read_excel(toString(data$datapath[row]), toString(data$sheet[row])) +# Create an nested directory for all the data and, if applicable, then further folders for graphs of interest +dir.create(paste(dirLocation(), "/", input$dirName, data$sheet[row], sep ="/")) +if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) +if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) +if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) +# Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis +if (sum(data$Mass) != 0) { +ylabel <-"Capacity (mAh/g)" +tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / data$Mass[row])) +tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)`* (1000 / data$Mass[row])) +tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c +tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 +} else { +ylabel <-"Capacity (Ah)" +tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` +tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 +} +tmp_excel$Cell <- row +tmp_excel$CE[is.infinite(tmp_excel$CE)|is.nan(tmp_excel$CE)|tmp_excel$CE > 200] <- 0; +# ###### +# +# This loop iterates through each cycle of the cell. +# +# ###### +cycles <- split(tmp_excel, tmp_excel$Cycle_Index) +prev_c <- 0 +lastCC <- 0 +ch_dch <- TRUE +durations <- vector(length = 4) +caps <- vector(length = 4) +prev <- TRUE +dchV <- 0 +chV <- 0 +i <- 1 +for (cycle in cycles) { +# ###### +# +# Within each cycle, take out the individual steps. These include the charge, discharge, and others. +# +# ###### +progress$set(detail = paste("Analyzing cell", row,", cycle", i)) +steps <- split(cycle, cycle$Step_Index) +n <- 1 +for (step in steps) { +# ###### +# +# Isolation of the charge and discharge cycles. The algorithm is as follows: +# +# If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines +# if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). +# +# ###### +if (abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) > 0.5) { +# ###### +# +# All code that should be executed for every charge/discharge cycles should be written here. +# +# ###### +lastCC <- n +if (step$'Current(A)'[[1]] > 0) { +chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) +dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) +dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) +durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] +caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] +ch_dch <- TRUE +} else { +dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`,1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) +dQDdV <- diff(step$`Discharge_Capacity(Ah)`)/diff(step$`Voltage(V)`) +durations[3] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] +caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] +ch_dch <- FALSE +if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { +dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) +prev_c = step$`Current(A)`[[1]] +} else { +dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) +} +} +} else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { +if (step$'Current(A)'[[1]] > 0) { +durations[2] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] +caps[2] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] +} else { +durations[4] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] +caps[4] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] +} +} +ch_dch <- FALSE +n <- n + 1 +} +# ###### +# +# Code meant to be run on data"per cycle" should be written here +# +# ###### +dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] +dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] +if (sum(data$Mass) != 0) { +DCap <- tail(cycle$Q.d, 1) +CCap <- tail(cycle$Q.c, 1) +} else { +DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) +CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) +} +timeCVFracCh <- durations[1] / (durations[1] + durations[2]) +timeCVFracDch <- durations[3] / (durations[3] + durations[4]) +capCVFracCh <- caps[1] / (caps[1] + caps[2]) +capCVFracDch <- caps[3] / (caps[3] + caps[4]) +# Record charge and discharge voltage, then calculate the delta and average voltage +cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, +dV=chV-dchV, DCap = DCap, CCap = CCap, CE = (CCap / DCap) * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, +cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, +capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) +i <- i + 1 +} +# ###### +# +# Code meant to be run on data"per cell" should be written here +# +# ###### +cell_data <- cycle_facts[cycle_facts$cell == row,] +# Discharge capacity plotting, with coulombic efficiency being plotted alongside +if (is.element("Discharge Capacity", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Capacity Plot.png", sep ="")) +eol <- cell_data$`DCap`[[1]] * 0.8 +plot(cell_data$cycle, cell_data$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) +abline(h=eol, lty ="dotted") +par(new = T) +plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) +mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") +axis(side = 4, col ="red", col.axis = "red") +dev.off() +} +# Discharge areal capacity plotting, with coulombic efficiency being plotted alongside +if (is.element("Discharge Areal Capacity", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) +new_par <- old_par <- par("mar") +new_par[4] <- old_par[2] +par(mar = new_par) +eol <- ((cell_data$DCap[[1]] * 1000) / data$area[row]) * 0.8 +plot(cell_data$cycle, ((cell_data$DCap * 1000) / data$area[row]), type ="p", main=paste("Discharge Areal Capacity for", input$dirName), xlab=NA, ylab="Discharge Capacity (mAh/cm^2)", mai = c(1,1,1,2)) +abline(h=eol, lty ="dotted") +par(new = T) +plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) +mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") +axis(side = 4, col ="red", col.axis = "red") +dev.off() +} +# Average voltage plotting +if (is.element("Average Voltage", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Average Voltage Plot.png", sep ="")) +plot(cell_data$cycle, cell_data$chV, col="blue", main=paste("Average Voltage Plot for", input$dirname, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_data[,2:4]), max(cell_data[,2:4]))) +points(cell_data$cycle, cell_data$dchV, col="red", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") +points(cell_data$cycle, cell_data$avgV, col="black", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") +legend("bottomright", c("Charge Voltage","Discharge Voltage","Average Voltage"), col=c("blue","red","black"), pch=19) +dev.off() +} +# Delta voltage plotting +if (is.element("Delta Voltage", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Delta Voltage Plot.png", sep ="")) +plot(cell_data$cycle, cell_data$dV, main=paste("Delta Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) +dev.off() +} +# Capacity Loss plotting +if (is.element("Capacity Loss", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Capacity Loss Plot.png", sep ="")) +plot(cell_data$cycle, cell_data$lostCap, main=paste("Capacity Loss Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_data$lostCap) + (2* sd(cell_data$lostCap)), mean(cell_data$lostCap) - (1.5* sd(cell_data$lostCap)))) +abline(h=median(cell_data$lostCap), lty="dotted") +dev.off() +} +if (is.element("dQdV Plots", input$gAnim)) { +dQdVplot <- function(){ +tmp_data <- dQdVData[dQdVData$cell == row,] +first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2,] +datalist <- split(tmp_data, tmp_data$cycle) +lapply(datalist, function(plotData){ +p <- plot(plotData$voltage, plotData$dQdV, main=paste("dQdV Plot for", input$dirName, data$sheet[row], "Cycle", plotData$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", +xlim = c(min(tmp_data$voltage), max(tmp_data$voltage)), ylim = c(min(tmp_data$dQdV), max(tmp_data$dQdV))) + +points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) +}) +} +save_gif(dQdVplot(), paste(dirLocation(), input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) +} +if (is.element("Voltage Profiles", input$gAnim)) { +vpPlot <- function(){ +first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2,] +datalist <- split(tmp_excel, tmp_excel$`Cycle_Index`) +lapply(datalist, function(plotData){ +p <- plot(plotData$CC, plotData$`Voltage(V)`, main=paste("Voltage Profile for", input$dirName, data$sheet[row], "Cycle", plotData$`Cycle_Index`[[1]]), xlab=ylabel, ylab= "Voltage (V)", +xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`))) + +points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) +}) +} +save_gif(vpPlot(), paste(dirLocation(), input$dirName, data$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) +} +# Save all data within the cell's directory +write.csv(tmp_excel, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) +write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," dQdV Data.csv", sep ="")) +# Append summation data to the larger datasets to be worked with later +final <- rbind(final, tmp_excel) +numCycles <<- rbind(numCycles, data.frame(sheet=data$sheet[row], cycles=nrow(cell_data))) +# Update progress bar +progress$set(value = row, detail = paste("Finished", row," of", nrow(data)," cells.")) +} +# ###### +# +# Code meant to be run on all data of all cells should be written here. +# +# ###### +# With iterations complete, final calculations are being worked +progress$set(detail ="Wrapping up...") +# Get the last status of each cycle for each cell (namely capacity) +DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) +CE <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(mean) +capSEs <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) +ceSEs <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(se) +stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) +# Send all the data to a global variable to be used elsewhere +total <<- final +tryCatch({ +# Total dishcharge capacity plotting +if (is.element("Total Discharge Capacity", input$gGraphs)) { +png(paste(dirLocation(), "/", input$dirName,"/", "Total Discharge Capacity Plot.png", sep ="")) +eol <- max(stats$DCap) * 0.8 +plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) +arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) +abline(h=eol, lty ="dotted") +par(new = T) +plot(stats$cycle, stats$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) +arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length=0.05, angle=90, code=3, col ="red") +axis(side = 4, col ="red") +mtext(side = 4, line = 2,"Coulombic Efficiency (%)") +dev.off() +} +}, error = function(cond) { +print(cond) +}) +# Save total data and stats +write.csv(stats, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Summary.csv", sep ="")) +write.csv(cycle_facts, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Cycle Facts.csv", sep ="")) +# If a histor directory does not exist, create it. Save all the data revelant to plotting to a RData file. +if (!dir.exists(paste(dirLocation(), "history", sep = "/"))) { +dir.create(paste(dirLocation(), "history", sep = "/")) +} +dirName <<- input$dirName +save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation(), "/history/", input$dirName, ".RData", sep = "")) +# Modal for completed analysis +shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation(), "/", input$dirName, sep = ""), +type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", +callbackR = function(x) { +if (x) { +export_to_origin() +} +} +) +# Finish progress bar +progress$set(value = nrow(data)) +# Re-enable all input fields, including graph builder +enable("files") +enable("lowV") +enable("highV") +enable("dirLocation()") +enable("submit") +enable("excelImport") +enable("gGraphs") +enable("peakFit") +enable("area") +enable("perActive") +enable("capActive") +enable("graphBuilder") +# Close progress bar +progress$close()}) +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +view(data) +View(raw_data) +View(data)_ +View(data) +View(data) +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +View(tmp_excel) +View(tmp_excel) +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +View(step) +tail(step$'Test_Time(s)', 1) +step$'Test_Time(s)'[[1]] +tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] +install.packages("lubridate") +library(lubridate) +period_to_seconds(tail(step$'Test_Time(s)', 1)) - period_to_seconds(step$'Test_Time(s)'[[1]]) +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') +devtools::uninstall('echemAnalyst') +setwd("C:/Users/rwaldhei/Documents/ecdpt") +devtools::create('echemAnalyst') +library(shiny); runApp('arbinimport.R') diff --git a/global.R b/global.R new file mode 100644 index 0000000..c442ca3 --- /dev/null +++ b/global.R @@ -0,0 +1,56 @@ +# ###### +# +# Welcome to the Battery Analyzer Utility! +# +# This script aims to make it quick and efficient to analyze data exported by an Arbin battery cycler +# +# ###### + +# ###### +# +# These are all the required packages to aid in several of the processes, ranging from data analysis to plotting +# +# ###### + +list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") +new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] +if(length(new.packages)) install.packages(new.packages) +lapply(list.of.packages, require, character.only = TRUE) + +source('ui.R', local = TRUE) +source('server.R') + +# This line tests if the current R environment is interactive, RStudio makes an interactive environment by default +if (interactive()) { + + # ###### + # + # All the global variables within the script, aka variables that need to be accessed by more than one + # function or session + # + #"Reactive Values" are ones that need to be readily changed, such as user inputs and variables to be displayed + # + # ###### + data <- reactiveValues(data = data.frame()) + final <- data.frame() + dirLocation <- reactiveVal("") + numCycles <- data.frame() + dQdVData <- data.frame() + total <- data.frame() + cycle_facts <- data.frame() + tmp_data <- data.frame() + dirName <<- "" + tmp_cycles <- vector() + titleLabel <-"" + xlabel <-"" + ylabel <-"" + addParams <- FALSE + arbinCM <- FALSE + catMetric <<- vector() + legTitle <<-"" + sheetName <<-"" + bounds <<- vector() + compCycleFacts <<- data.frame() +} + +shinyApp(ui, server) diff --git a/arbinimport.R b/server.R similarity index 68% rename from arbinimport.R rename to server.R index aff5f30..5d2d58c 100644 --- a/arbinimport.R +++ b/server.R @@ -1,170 +1,7 @@ -# ###### -# -# Welcome to the Battery Analyzer Utility! -# -# This script aims to make it quick and efficient to analyze data exported by an Arbin battery cycler -# -# ###### - -# ###### -# -# These are all the required packages to aid in several of the processes, ranging from data analysis to plotting -# -# ###### +library(shiny) -list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") -new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] -if(length(new.packages)) install.packages(new.packages) -lapply(list.of.packages, require, character.only = TRUE) +server <- function(input, output, session) { -# This line tests if the current R environment is interactive, RStudio makes an interactive environment by default -if (interactive()) { - - # ###### - # - # All the global variables within the script, aka variables that need to be accessed by more than one - # function or session - # - #"Reactive Values" are ones that need to be readily changed, such as user inputs and variables to be displayed - # - # ###### - data <- reactiveValues(data = data.frame()) - final <- data.frame() - dirLocation <- reactiveVal("") - numCycles <- data.frame() - dQdVData <- data.frame() - total <- data.frame() - cycle_facts <- data.frame() - tmp_data <- data.frame() - dirName <<- "" - tmp_cycles <- vector() - titleLabel <-"" - xlabel <-"" - ylabel <-"" - addParams <- FALSE - arbinCM <- FALSE - catMetric <<- vector() - legTitle <<-"" - sheetName <<-"" - bounds <<- vector() - compCycleFacts <<- data.frame() - - # ###### - # - # This is the UI function for Shiny, it defines how the layout of what the user sees - # - # ###### - ui <- fluidPage( - - # Utilizing javascript within Shiny allows for addd features such as enable/disable of inputs - useShinyjs(), - - # Shinyalert is a package that makes interactive"pop-ups" (modals) easy to generate - useShinyalert(), - - setBackgroundColor( - color = c("ghostwhite", "lightgrey"), - gradient = "linear", - direction = "bottom", - shinydashboard = FALSE - ), - - fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), - - fluidRow(div(style = "color: red; font-size: 20pt;", "!!! Pay Attention. Controls Have Moved !!!"), align = "center"), - - # This first column is where most user inputs are, with the exception of the directory name - column(4, align = "center", - - # This generates the optional block in which the user can import a previous R environment - fluidRow( - strong("Start Here"), tags$br(), - "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), - actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width:80%; margin:5%; font-size:100%"), tags$br(), - helpText("The analysis will create a folder within the selected folder."), - style = "border: 1px solid black; padding: 5%; margin:5%" - ), - - fluidRow( - fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), - actionButton("load", "Load"), - style = "border: 1px dashed black; margin: 5%; padding: 5%" - ), - - # These are the"optional" parameters that need to be filled out if select graphs are selected - fluidRow( - strong("Optional Parameters"), tags$br(), - "Parameters responsible for certain graphs.", tags$br(), tags$br(), - - # Used for dishcharge areal capacity graphs - numericInput("area","Limiting Electrode Area (cm^2)", 2.74, min = 0), - - # Used for C-Rate calculations - # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), - # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), - style ="border: 1px dashed black; padding: 5%; margin:5%" - ), - ), - - # The second column is where selection of graphs and further features are selected - column(4, align ="left", - fluidRow( - # Presents options for graphs to be generated - "Choose graphs to be generated:", - actionButton("whatGraph","What's this?", class ="btn-link"), - checkboxGroupInput("gGraphs", NULL, choices = c("Discharge Capacity","Discharge Areal Capacity", - "Total Discharge Capacity","Average Voltage","Delta Voltage","Capacity Loss"), inline = FALSE), - "Choose graphs to animate:", - checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), - "Advanced Analysis", - #radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), - #helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), - style ="margin: 5%; border: 1px solid black; padding: 5%" - ), - ), - - # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process - column(4, align ="center", - fluidRow( - selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), - strong("Files to be Analyzed*"), tags$br(), - "Import all Arbin files of interest.", tags$br(), tags$br(), - fileInput("files", NULL, multiple = TRUE), - actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width:80%; height:100px; margin:5%; font-size:100%"), - style = "border: 4px double black; padding: 5%; margin:5%" - ), - - # This final block enables a button after data becomes available, which trigger the modal to build custom graphs - fluidRow( - strong("Custom Graph Builder"), tags$br(), - "Customize Graphs Once Data is Available", - disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height:50px; margin:5%; font-size:100%")), tags$br(), - style ="border: 1px solid black; padding: 5%; margin:5%" - ), - - fluidRow( - strong("Optional: Import Active Material Masses from Excel"), tags$br(), - "Running Analysis without Masses Will Render Raw Capacities (Ah)", - textAreaInput("masses", NULL, height = "100px", resize = "vertical"), - helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), - actionButton("excelImport", "Import"), - style = "border: 1px dashed black; padding: 5%; margin:5%" - ), - ), - - # This renders the summary datatable at the bottom of the interface once data is imported - fluidRow( - dataTableOutput("channels") - ) - ) - - # ###### - # - # This is the server functon of Shiny. It defines all the"processing" of the data that the user initiated through the interface - # - # ###### - server <- function(input, output, session) { - # This sets the maximum file size Shiny will import, the default of 5Mb is not large enough to handle Arbin files options(shiny.maxRequestSize=1000*1024^2) @@ -191,95 +28,95 @@ if (interactive()) { # Defines the modal in which the cell masses can be exported from Excel graphModal <- modalDialog({ fluidPage(style ="font-size:15pt;", - tags$head(tags$style(".modal-dialog{min-width:60%}")), - - fluidRow(align ="center", - HTML(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GraphX AxisY AxisPlot Frequency
Description
dQdV GraphVoltage (V)dQdV (Ah/V)per cycleThe differential capacity plot for each cycle
Voltage ProfileContinuous Capacity (mAh/g or Ah)Voltage (V)per cycleVoltage vs. capacity plot for each cycle. Units depend if the masses are specified.
Voltage vs. TimeTime (min)Voltage (V)per cycleThe voltage as a function of time, including all steps
Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per cellDischarge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.
Discharge Areal CapacityCycleDischarge Capacity (Ah/cm2)per cellDischarge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.
Total Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per analysisDischarge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.
Average VoltageCycleVoltage (V)per cellThe average voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The average voltage is then (Vcharge + Vdischarge)/2. Charge and discharge voltages are plotted alongside the average.
Delta VoltageCycleVoltage (V)per cellThe delta voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The delta voltage is then Vcharge - Vdischarge). Charge and discharge voltages are plotted alongside the average.
Capacity LossCycleCapacity (mAh/g or Ah)per cellThe discharge capacity minus the charge capacity for each cycle. Units depend if the masses are specified.
- ') - ), + tags$head(tags$style(".modal-dialog{min-width:60%}")), + + fluidRow(align ="center", + HTML(' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GraphX AxisY AxisPlot Frequency
Description
dQdV GraphVoltage (V)dQdV (Ah/V)per cycleThe differential capacity plot for each cycle
Voltage ProfileContinuous Capacity (mAh/g or Ah)Voltage (V)per cycleVoltage vs. capacity plot for each cycle. Units depend if the masses are specified.
Voltage vs. TimeTime (min)Voltage (V)per cycleThe voltage as a function of time, including all steps
Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per cellDischarge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.
Discharge Areal CapacityCycleDischarge Capacity (Ah/cm2)per cellDischarge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.
Total Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per analysisDischarge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.
Average VoltageCycleVoltage (V)per cellThe average voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The average voltage is then (Vcharge + Vdischarge)/2. Charge and discharge voltages are plotted alongside the average.
Delta VoltageCycleVoltage (V)per cellThe delta voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The delta voltage is then Vcharge - Vdischarge). Charge and discharge voltages are plotted alongside the average.
Capacity LossCycleCapacity (mAh/g or Ah)per cellThe discharge capacity minus the charge capacity for each cycle. Units depend if the masses are specified.
+ ') + ), )}, title ="Graph Types", easyClose = TRUE) # Ensures the files imported for analysis are Excel files @@ -313,7 +150,7 @@ if (interactive()) { base_names <- append(base_names, split_path(folder)[1]) } if ("history" %in% base_names) { - load(paste(dirLocation(), "/history/", "RateCap.RData", sep ="")) + load(paste(dirLocation(), "/history/", "Formation.RData", sep ="")) data <<- data @@ -325,7 +162,7 @@ if (interactive()) { output$currDir <- renderText({paste(split_path(dirLocation())[1], "(", split_path(dirLocation())[2], ")")}) } }) - + graphbuilder <- modalDialog({ fluidPage( useShinyjs(), @@ -335,26 +172,26 @@ if (interactive()) { tags$head(tags$style(".modal-body{ min-height:1000px}")), sidebarLayout( - + sidebarPanel( fluidRow( headerPanel("Graph Options"), ), fluidRow(style = "padding:5%; border: 1px solid black;", - strong("Basis for Plot Types"), tags$br(), - helpText("Between Analysis requires a second dataset to be imported"), tags$br(), - radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), - hidden(fileInput("compAnalysis", "Data to Compare")) + strong("Basis for Plot Types"), tags$br(), + helpText("Between Analysis requires a second dataset to be imported"), tags$br(), + radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), + hidden(fileInput("compAnalysis", "Data to Compare")) ), fluidRow(style ="padding:5%; margin:5%;", - radioButtons("typeGraph","Graph Type:", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", - "Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" ), inline = FALSE), - radioButtons("plotStyle","Plot Style:", choiceNames = c("Point","Line","Both"), choiceValues = c("p","l","o"), inline = TRUE), - checkboxGroupInput("cells","Cell to Analyze:", choices = 1, inline = FALSE), - selectInput("renderCycles","Cycles of Interest:", choices = 1, multiple = TRUE), + radioButtons("typeGraph","Graph Type:", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" ), inline = FALSE), + radioButtons("plotStyle","Plot Style:", choiceNames = c("Point","Line","Both"), choiceValues = c("p","l","o"), inline = TRUE), + checkboxGroupInput("cells","Cell to Analyze:", choices = 1, inline = FALSE), + selectInput("renderCycles","Cycles of Interest:", choices = 1, multiple = TRUE), ), fluidRow( @@ -365,42 +202,42 @@ if (interactive()) { textOutput("hoverCoordy", inline = TRUE), style ="border: 1px solid black; padding: 5%; margin: 5%;" ), - + fluidRow( - textInput("fileName","Name of graph file:"), - actionButton("saveGraph","Save Graph", width = '100%', class = 'btn-primary'), - style ="border: 4px double black; padding: 5%; margin: 5%;" + textInput("fileName","Name of graph file:"), + actionButton("saveGraph","Save Graph", width = '100%', class = 'btn-primary'), + style ="border: 4px double black; padding: 5%; margin: 5%;" ), ), mainPanel( - fluidRow( - plotOutput("outputPlot", height ="800px", click = "plot_click"), - style = "padding: 5%;", - ), - fluidRow( - h3("Graph Formatting"), - helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), - column(2, - numericInput("xMin", "X Min", value = NULL), - sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), - ), - column(2, - numericInput("xMax", "X Max", value = NULL), - sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), - ), - column(2, - numericInput("yMin", "Y Min", value = NULL), - ), - column(2, - numericInput("yMax", "Y Max", value = NULL), - ), - column(4, - textInput("originalData", "Original Data Name", value = "Original Data"), - textInput("compareData", "Comparison Data Name", value = "Comparison Data"), - ), - style ="border: 1px dashed black; padding: 2%;", - ) + fluidRow( + plotOutput("outputPlot", height ="800px", click = "plot_click"), + style = "padding: 5%;", + ), + fluidRow( + h3("Graph Formatting"), + helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), + column(2, + numericInput("xMin", "X Min", value = NULL), + sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column(2, + numericInput("xMax", "X Max", value = NULL), + sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column(2, + numericInput("yMin", "Y Min", value = NULL), + ), + column(2, + numericInput("yMax", "Y Max", value = NULL), + ), + column(4, + textInput("originalData", "Original Data Name", value = "Original Data"), + textInput("compareData", "Comparison Data Name", value = "Comparison Data"), + ), + style ="border: 1px dashed black; padding: 2%;", + ) ) ) ) @@ -590,7 +427,7 @@ if (interactive()) { if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) - + # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis if (sum(data$Mass) != 0) { ylabel <-"Capacity (mAh/g)" @@ -689,7 +526,7 @@ if (interactive()) { ch_dch <- FALSE n <- n + 1 } - + # ###### # # Code meant to be run on data"per cycle" should be written here @@ -763,7 +600,7 @@ if (interactive()) { axis(side = 4, col ="red", col.axis = "red") dev.off() } - + # Average voltage plotting if (is.element("Average Voltage", input$gGraphs)) { @@ -798,7 +635,7 @@ if (interactive()) { lapply(datalist, function(plotData){ p <- plot(plotData$voltage, plotData$dQdV, main=paste("dQdV Plot for", input$dirName, data$sheet[row], "Cycle", plotData$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", xlim = c(min(tmp_data$voltage), max(tmp_data$voltage)), ylim = c(min(tmp_data$dQdV), max(tmp_data$dQdV))) + - points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) + points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) }) } save_gif(dQdVplot(), paste(dirLocation(), input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) @@ -819,7 +656,7 @@ if (interactive()) { # Save all data within the cell's directory write.csv(tmp_excel, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) - + # Append summation data to the larger datasets to be worked with later final <- rbind(final, tmp_excel) numCycles <<- rbind(numCycles, data.frame(sheet=data$sheet[row], cycles=nrow(cell_data))) @@ -888,7 +725,7 @@ if (interactive()) { export_to_origin() } } - ) + ) # Finish progress bar progress$set(value = nrow(data)) @@ -916,10 +753,10 @@ if (interactive()) { disable("area") disable("perActive") disable("capActive") - + choices <- c("dQdV Graphs","Voltage Profiles","Voltage vs. Time","Discharge Capacity","Discharge Areal Capacity", - "Total Discharge Capacity","Average Voltage","Delta Voltage") - + "Total Discharge Capacity","Average Voltage","Delta Voltage") + if (is.element("Discharge Areal Capacity", input$gGraphs)) { enable("area") } @@ -954,7 +791,7 @@ if (interactive()) { cellIndex <- match(input$cells, numCycles$sheet) switch(input$typeGraph, - "dQdV Graphs" = { + "dQdV Graphs" = { tmp_data <<- data.frame(x=dQdVData[dQdVData$cell %in% cellIndex,]$voltage, y=dQdVData[dQdVData$cell %in% cellIndex,]$dQdV, cycle=dQdVData[dQdVData$cell %in% cellIndex,]$cycle, cell=dQdVData[dQdVData$cell %in% cellIndex,]$cell) tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)),] @@ -962,7 +799,7 @@ if (interactive()) { xlabel <<-"Voltage (V)" ylabel <<-"dQdV (mAh/V)" }, - "Voltage Profiles" = { + "Voltage Profiles" = { tmp_data <<- data.frame(x=(-1) * total[total$Cell %in% cellIndex,]$CC, y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)),] @@ -974,7 +811,7 @@ if (interactive()) { } ylabel <<-"Voltage (V)" }, - "Voltage vs. Time" = { + "Voltage vs. Time" = { tmp_data <<- data.frame(x=(total[total$Cell %in% cellIndex,]$`Test_Time(s)` / 60), y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) tmp_data <<- tmp_data[tmp_data$cycle %in% input$renderCycles,] @@ -991,56 +828,56 @@ if (interactive()) { xlabel <<-"Time (min)" ylabel <<-"Voltage (V)" }, - "Charge Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$chV, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, cell = cycle_facts[cycle_facts$cell %in% cellIndex,]$cell) - - titleLabel <<- "Charge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Discharge Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dchV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Discharge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Average Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$avgV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Average Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Delta Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Delta Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Discharge Capacity" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$DCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Discharge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Discharge Capacity (mAh/g)" - } else { - ylabel <<- "Discharge Capacity (Ah)" - } - }, - "Charge Capacity" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$CCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Charge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Charge Capacity (mAh/g)" - } else { - ylabel <<- "Charge Capacity (Ah)" - } - } + "Charge Voltage" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$chV, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, cell = cycle_facts[cycle_facts$cell %in% cellIndex,]$cell) + + titleLabel <<- "Charge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Discharge Voltage" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dchV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) + + titleLabel <<- "Discharge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Average Voltage" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$avgV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) + + titleLabel <<- "Average Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Delta Voltage" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) + + titleLabel <<- "Delta Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Discharge Capacity" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$DCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) + + titleLabel <<- "Discharge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Discharge Capacity (mAh/g)" + } else { + ylabel <<- "Discharge Capacity (Ah)" + } + }, + "Charge Capacity" = { + tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$CCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) + + titleLabel <<- "Charge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Charge Capacity (mAh/g)" + } else { + ylabel <<- "Charge Capacity (Ah)" + } + } ) tmp_data$color <<- sapply(tmp_data$cycle, function(x) {match(x, input$renderCycles, nomatch = 1)}) @@ -1057,7 +894,7 @@ if (interactive()) { tryCatch({ comp_chV <- compCycleFacts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(mean) comp_chVSE <- compCycleFacts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(se) - + tmp_data <<- data.frame(x=c(chV$cycle, comp_chV$cycle), y=c(chV$chV, comp_chV$chV), se=c(chVSE$chV, comp_chVSE$chV), cell=c(rep(1, length(chV$cycle)), rep(2, length(comp_chV$cycle))), cycle=c(chV$cycle, comp_chV$cycle)) tmp_data$symbol <<- rep(1, nrow(tmp_data)) tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) @@ -1069,48 +906,48 @@ if (interactive()) { error = function(x) { print(x) }) - }, - "Discharge Voltage" = { - x<-0 - dchV <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) - dchVSE <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) + }, + "Discharge Voltage" = { + x<-0 + dchV <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) + dchVSE <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) + + tryCatch({ + comp_dchV <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) + comp_dchVSE <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) + + tmp_data <<- data.frame(x=c(dchV$cycle, comp_dchV$cycle), y=c(dchV$dchV, comp_dchV$dchV), se=c(dchVSE$dchV, comp_dchVSE$dchV), cell=c(rep(1, length(dchV$cycle)), rep(2, length(comp_dchV$cycle))), cycle=c(dchV$cycle, comp_dchV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - tryCatch({ - comp_dchV <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_dchVSE <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(dchV$cycle, comp_dchV$cycle), y=c(dchV$dchV, comp_dchV$dchV), se=c(dchVSE$dchV, comp_dchVSE$dchV), cell=c(rep(1, length(dchV$cycle)), rep(2, length(comp_dchV$cycle))), cycle=c(dchV$cycle, comp_dchV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Disharge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) + titleLabel <<- "Disharge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" }, - "Average Voltage" = { - avgV <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) - avgVSE <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) + error = function(x) { + print(x) + }) + }, + "Average Voltage" = { + avgV <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) + avgVSE <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) + + tryCatch({ + comp_avgV <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) + comp_avgVSE <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) + + tmp_data <<- data.frame(x=c(avgV$cycle, comp_avgV$cycle), y=c(avgV$avgV, comp_avgV$avgV), se=c(avgVSE$avgV, comp_avgVSE$avgV), cell=c(rep(1, length(avgV$cycle)), rep(2, length(comp_avgV$cycle))), cycle=c(avgV$cycle, comp_avgV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - tryCatch({ - comp_avgV <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_avgVSE <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(avgV$cycle, comp_avgV$cycle), y=c(avgV$avgV, comp_avgV$avgV), se=c(avgVSE$avgV, comp_avgVSE$avgV), cell=c(rep(1, length(avgV$cycle)), rep(2, length(comp_avgV$cycle))), cycle=c(avgV$cycle, comp_avgV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Average Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) + titleLabel <<- "Average Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" }, + error = function(x) { + print(x) + }) + }, "Delta Voltage" = { dV <- cycle_facts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(mean) dVSE <- cycle_facts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(se) @@ -1294,8 +1131,8 @@ if (interactive()) { observeEvent(input$perType, { if (input$perType == "Within Analysis") { updateRadioButtons(session, "typeGraph", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", - "Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" )) + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" )) show("cells") show("renderCycles") @@ -1346,6 +1183,4 @@ if (interactive()) { shinyalert("Success!", paste("Plot saved in working directory:\n", getwd()),"success") }) - } -} -shinyApp(ui, server) +} \ No newline at end of file diff --git a/ui.R b/ui.R new file mode 100644 index 0000000..cbe0a05 --- /dev/null +++ b/ui.R @@ -0,0 +1,108 @@ +library(shiny) + +ui <- shinyUI({ + fluidPage( + + # Utilizing javascript within Shiny allows for addd features such as enable/disable of inputs + useShinyjs(), + + # Shinyalert is a package that makes interactive"pop-ups" (modals) easy to generate + useShinyalert(), + + setBackgroundColor( + color = c("ghostwhite", "lightgrey"), + gradient = "linear", + direction = "bottom", + shinydashboard = FALSE + ), + + fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), + + fluidRow(div(style = "color: red; font-size: 20pt;", "!!! Pay Attention. Controls Have Moved !!!"), align = "center"), + + # This first column is where most user inputs are, with the exception of the directory name + column(4, align = "center", + + # This generates the optional block in which the user can import a previous R environment + fluidRow( + strong("Start Here"), tags$br(), + "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), + actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width:80%; margin:5%; font-size:100%"), tags$br(), + helpText("The analysis will create a folder within the selected folder."), + style = "border: 1px solid black; padding: 5%; margin:5%" + ), + + fluidRow( + fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), + actionButton("load", "Load"), + style = "border: 1px dashed black; margin: 5%; padding: 5%" + ), + + # These are the"optional" parameters that need to be filled out if select graphs are selected + fluidRow( + strong("Optional Parameters"), tags$br(), + "Parameters responsible for certain graphs.", tags$br(), tags$br(), + + # Used for dishcharge areal capacity graphs + numericInput("area","Limiting Electrode Area (cm^2)", 2.74, min = 0), + + # Used for C-Rate calculations + # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), + # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), + style ="border: 1px dashed black; padding: 5%; margin:5%" + ), + ), + + # The second column is where selection of graphs and further features are selected + column(4, align ="left", + fluidRow( + # Presents options for graphs to be generated + "Choose graphs to be generated:", + actionButton("whatGraph","What's this?", class ="btn-link"), + checkboxGroupInput("gGraphs", NULL, choices = c("Discharge Capacity","Discharge Areal Capacity", + "Total Discharge Capacity","Average Voltage","Delta Voltage","Capacity Loss"), inline = FALSE), + "Choose graphs to animate:", + checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), + "Advanced Analysis", + #radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), + #helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), + style ="margin: 5%; border: 1px solid black; padding: 5%" + ), + ), + + # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process + column(4, align ="center", + fluidRow( + selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), + strong("Files to be Analyzed*"), tags$br(), + "Import all Arbin files of interest.", tags$br(), tags$br(), + fileInput("files", NULL, multiple = TRUE), + actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width:80%; height:100px; margin:5%; font-size:100%"), + style = "border: 4px double black; padding: 5%; margin:5%" + ), + + # This final block enables a button after data becomes available, which trigger the modal to build custom graphs + fluidRow( + strong("Custom Graph Builder"), tags$br(), + "Customize Graphs Once Data is Available", + disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height:50px; margin:5%; font-size:100%")), tags$br(), + style ="border: 1px solid black; padding: 5%; margin:5%" + ), + + fluidRow( + strong("Optional: Import Active Material Masses from Excel"), tags$br(), + "Running Analysis without Masses Will Render Raw Capacities (Ah)", + textAreaInput("masses", NULL, height = "100px", resize = "vertical"), + helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), + actionButton("excelImport", "Import"), + style = "border: 1px dashed black; padding: 5%; margin:5%" + ), + ), + + # This renders the summary datatable at the bottom of the interface once data is imported + fluidRow( + dataTableOutput("channels") + ) +) +} +) \ No newline at end of file From 5f23a54a777a5f8b98c6bd31d99cc24cbfe69c6c Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Wed, 18 Aug 2021 16:57:35 -0400 Subject: [PATCH 09/19] assewmbling multiple groups (v broken) --- global.R | 5 +- server.R | 222 +++++++++++++++++++++---------------------------------- ui.R | 14 ++-- 3 files changed, 95 insertions(+), 146 deletions(-) diff --git a/global.R b/global.R index c442ca3..0905c18 100644 --- a/global.R +++ b/global.R @@ -12,6 +12,8 @@ # # ###### +options(warn = 2) + list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] if(length(new.packages)) install.packages(new.packages) @@ -19,6 +21,7 @@ lapply(list.of.packages, require, character.only = TRUE) source('ui.R', local = TRUE) source('server.R') +source('info.R') # This line tests if the current R environment is interactive, RStudio makes an interactive environment by default if (interactive()) { @@ -33,7 +36,7 @@ if (interactive()) { # ###### data <- reactiveValues(data = data.frame()) final <- data.frame() - dirLocation <- reactiveVal("") + dirLocation <- vector() numCycles <- data.frame() dQdVData <- data.frame() total <- data.frame() diff --git a/server.R b/server.R index 5d2d58c..fd46e72 100644 --- a/server.R +++ b/server.R @@ -9,6 +9,18 @@ server <- function(input, output, session) { se <- function(x) {sd(x) / length(x)} + proxy = dataTableProxy("channels") + + batch.elements <- c("rerun", "load", "area", "whatGraph", "gGraphs", "gAnim", "graphBuilder") + + observeEvent(input$batchProcessing, { + if (input$batchProcessing) { + lapply(batch.elements, hide) + } else { + lapply(batch.elements, show) + } + }) + export_to_origin <- function() { if (!("reticulate" %in% installed.packages()[, "Package"])) { install.packages("reticulate") @@ -20,7 +32,7 @@ server <- function(input, output, session) { py_install("OriginExt", pip = TRUE) py_install("pandas", pip = TRUE) - filtered_location <- shQuote(paste(dirLocation(), "/", input$dirName, sep = '')) + filtered_location <- shQuote(paste(dirLocation, "/", input$dirName, sep = '')) system(paste(py_location$python, " rPyO.py ", filtered_location, sep='')) } @@ -30,101 +42,16 @@ server <- function(input, output, session) { fluidPage(style ="font-size:15pt;", tags$head(tags$style(".modal-dialog{min-width:60%}")), - fluidRow(align ="center", - HTML(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GraphX AxisY AxisPlot Frequency
Description
dQdV GraphVoltage (V)dQdV (Ah/V)per cycleThe differential capacity plot for each cycle
Voltage ProfileContinuous Capacity (mAh/g or Ah)Voltage (V)per cycleVoltage vs. capacity plot for each cycle. Units depend if the masses are specified.
Voltage vs. TimeTime (min)Voltage (V)per cycleThe voltage as a function of time, including all steps
Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per cellDischarge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.
Discharge Areal CapacityCycleDischarge Capacity (Ah/cm2)per cellDischarge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.
Total Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per analysisDischarge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.
Average VoltageCycleVoltage (V)per cellThe average voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The average voltage is then (Vcharge + Vdischarge)/2. Charge and discharge voltages are plotted alongside the average.
Delta VoltageCycleVoltage (V)per cellThe delta voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The delta voltage is then Vcharge - Vdischarge). Charge and discharge voltages are plotted alongside the average.
Capacity LossCycleCapacity (mAh/g or Ah)per cellThe discharge capacity minus the charge capacity for each cycle. Units depend if the masses are specified.
- ') - ), + fluidRow(align ="center", graphInfoTable), + )}, title ="Graph Types", easyClose = TRUE) # Ensures the files imported for analysis are Excel files observeEvent(input$files, { validFile <- FALSE - for (file in input$files) { - if (file_ext(file) == "xlsx" | file_ext(file) == "xls") { + for (file in 1:nrow(input$files)) { + if (file_ext(input$files[file, "name"]) == "xlsx" | file_ext(input$files[file,]) == "xls") { validFile <- TRUE arbinCM <<- FALSE } else if (file_ext(file) == "csv") { @@ -142,24 +69,38 @@ server <- function(input, output, session) { observeEvent(input$chooseDir, { chosenDir <- tk_choose.dir() - dirLocation(chosenDir) + x<- 0 + #if (input$batchProcessing) { + # append(dirLocation, chosenDir) + #} else { + dirLocation <- chosenDir + #} - internal_folders <- list.dirs(path = dirLocation(), recursive = FALSE) + internal_folders <- list.dirs(path = dirLocation, recursive = FALSE) base_names = vector() for (folder in internal_folders) { base_names <- append(base_names, split_path(folder)[1]) } + if ("history" %in% base_names) { - load(paste(dirLocation(), "/history/", "Formation.RData", sep ="")) + past <- new.env() - data <<- data + load(paste(dirLocation, "/history/", "Formation.RData", sep =""), envir = past) - output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), - colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + if (exists("past$group")) { + if (input$batchProcessing) { + data <<- rbind(data, past$data) + } else { + data <<- past$data + } + + output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(5)))), + colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + } } - if (!is.na(dirLocation())) { - output$currDir <- renderText({paste(split_path(dirLocation())[1], "(", split_path(dirLocation())[2], ")")}) + if (!is.na(dirLocation)) { + output$currDir <- renderText({paste(split_path(dirLocation)[1], "(", split_path(dirLocation)[2], ")")}) } }) @@ -258,14 +199,14 @@ server <- function(input, output, session) { if (validFile) { data <<- filter(data, grepl('Channel', sheet)) - dirLocation(dirLocation()) + dirLocation(dirLocation) numCycles <<- numCycles dQdVData <<- dQdVData total <<- total cycle_facts <<- cycle_facts output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), - colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) enable("graphBuilder") } else { @@ -289,27 +230,31 @@ server <- function(input, output, session) { } else { for (i in 1:nrow(files)) { sheets <- excel_sheets(files[i, 4]) - file_sheet <- rbind(file_sheet, data.frame(name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), + file_sheet <- rbind(file_sheet, data.frame(group = basename(dirLocation), name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) } raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) } - if (!is.null(dim(data)[1])) { - new_rows <- intersect(raw_data$sheet, data$sheet) + if (!is.null(dim(data)[1]) & !is.null(size(raw_data))) { + new_rows <- intersect(raw_data$sheet, data[data["group"] == basename(dirLocation)]$sheet) data <<- data[which(data$sheet %in% new_rows),] data$datapath <<- raw_data$datapath } else { - data <<- raw_data + if (input$batchProcessing) { + data <<- rbind(data, raw_data) + } else { + data <<- raw_data + } } if (arbinCM) { output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), colnames = c("File", "Sheet", "Filepath")) } else { - output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(4)))), - colnames = c("File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5)))), + colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) } } @@ -332,7 +277,6 @@ server <- function(input, output, session) { shinyalert("Something isn't right...","The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.","error") removeModal() }, finally = { - proxy = dataTableProxy("channels") replaceData(proxy, data) renderDataTable(data) @@ -346,7 +290,7 @@ server <- function(input, output, session) { observeEvent(input$submit, { if (length(names(data)) <= 1) { shinyalert("Uh oh!", "You need to import cells first!", "error") - } else if (is.na(dirLocation()) | dirLocation() == "") { + } else if (is.na(dirLocation) | dirLocation == "") { shinyalert("Uh oh!", "You need to enter a directory name first!", "error") } else if (input$dirName == "") { shinyalert("Uh oh!", "You need to enter an analysis name first!", "error") @@ -386,7 +330,7 @@ server <- function(input, output, session) { disable("files") disable("lowV") disable("highV") - disable("dirLocation()") + disable("dirLocation") disable("submit") disable("excelImport") disable("gGraphs") @@ -396,7 +340,7 @@ server <- function(input, output, session) { disable("capActive") # Creates the directory in which all data will be stored - dir.create(paste(dirLocation(), input$dirName, sep = "/")) + dir.create(paste(dirLocation, input$dirName, sep = "/")) # Update the status once all set-up functions are complete progress$set(detail ="Starting first cell...") @@ -423,10 +367,10 @@ server <- function(input, output, session) { } # Create an nested directory for all the data and, if applicable, then further folders for graphs of interest - dir.create(paste(dirLocation(), "/", input$dirName, data$sheet[row], sep ="/")) - if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) - if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) - if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) + dir.create(paste(dirLocation, "/", input$dirName, data$sheet[row], sep ="/")) + if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) + if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) + if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis if (sum(data$Mass) != 0) { @@ -574,7 +518,7 @@ server <- function(input, output, session) { # Discharge capacity plotting, with coulombic efficiency being plotted alongside if (is.element("Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Capacity Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Capacity Plot.png", sep ="")) eol <- cell_data$`DCap`[[1]] * 0.8 plot(cell_data$cycle, cell_data$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) abline(h=eol, lty ="dotted") @@ -587,7 +531,7 @@ server <- function(input, output, session) { # Discharge areal capacity plotting, with coulombic efficiency being plotted alongside if (is.element("Discharge Areal Capacity", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) new_par <- old_par <- par("mar") new_par[4] <- old_par[2] par(mar = new_par) @@ -604,7 +548,7 @@ server <- function(input, output, session) { # Average voltage plotting if (is.element("Average Voltage", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Average Voltage Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Average Voltage Plot.png", sep ="")) plot(cell_data$cycle, cell_data$chV, col="blue", main=paste("Average Voltage Plot for", input$dirname, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_data[,2:4]), max(cell_data[,2:4]))) points(cell_data$cycle, cell_data$dchV, col="red", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") points(cell_data$cycle, cell_data$avgV, col="black", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") @@ -614,14 +558,14 @@ server <- function(input, output, session) { # Delta voltage plotting if (is.element("Delta Voltage", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Delta Voltage Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Delta Voltage Plot.png", sep ="")) plot(cell_data$cycle, cell_data$dV, main=paste("Delta Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) dev.off() } # Capacity Loss plotting if (is.element("Capacity Loss", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Capacity Loss Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Capacity Loss Plot.png", sep ="")) plot(cell_data$cycle, cell_data$lostCap, main=paste("Capacity Loss Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_data$lostCap) + (2* sd(cell_data$lostCap)), mean(cell_data$lostCap) - (1.5* sd(cell_data$lostCap)))) abline(h=median(cell_data$lostCap), lty="dotted") dev.off() @@ -638,7 +582,7 @@ server <- function(input, output, session) { points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) }) } - save_gif(dQdVplot(), paste(dirLocation(), input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) + save_gif(dQdVplot(), paste(dirLocation, input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) } if (is.element("Voltage Profiles", input$gAnim)) { @@ -651,11 +595,11 @@ server <- function(input, output, session) { points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) }) } - save_gif(vpPlot(), paste(dirLocation(), input$dirName, data$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) + save_gif(vpPlot(), paste(dirLocation, input$dirName, data$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) } # Save all data within the cell's directory - write.csv(tmp_excel, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) + write.csv(tmp_excel, file = paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) # Append summation data to the larger datasets to be worked with later final <- rbind(final, tmp_excel) @@ -687,7 +631,7 @@ server <- function(input, output, session) { tryCatch({ # Total dishcharge capacity plotting if (is.element("Total Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation(), "/", input$dirName,"/", "Total Discharge Capacity Plot.png", sep ="")) + png(paste(dirLocation, "/", input$dirName,"/", "Total Discharge Capacity Plot.png", sep ="")) eol <- max(stats$DCap) * 0.8 plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) @@ -704,21 +648,21 @@ server <- function(input, output, session) { }) # Save total data and stats - write.csv(stats, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Summary.csv", sep ="")) - if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," dQdV Data.csv", sep ="")) - write.csv(cycle_facts, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Cycle Facts.csv", sep ="")) + write.csv(stats, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," Summary.csv", sep ="")) + if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," dQdV Data.csv", sep ="")) + write.csv(cycle_facts, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," Cycle Facts.csv", sep ="")) # If a histor directory does not exist, create it. Save all the data revelant to plotting to a RData file. - if (!dir.exists(paste(dirLocation(), "history", sep = "/"))) { - dir.create(paste(dirLocation(), "history", sep = "/")) + if (!dir.exists(paste(dirLocation, "history", sep = "/"))) { + dir.create(paste(dirLocation, "history", sep = "/")) } dirName <<- input$dirName - save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation(), "/history/", input$dirName, ".RData", sep = "")) + save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", input$dirName, ".RData", sep = "")) # Modal for completed analysis - shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation(), "/", input$dirName, sep = ""), + shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation, "/", input$dirName, sep = ""), type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", callbackR = function(x) { if (x) { @@ -734,7 +678,7 @@ server <- function(input, output, session) { enable("files") enable("lowV") enable("highV") - enable("dirLocation()") + enable("dirLocation") enable("submit") enable("excelImport") enable("gGraphs") @@ -800,7 +744,7 @@ server <- function(input, output, session) { ylabel <<-"dQdV (mAh/V)" }, "Voltage Profiles" = { - tmp_data <<- data.frame(x=(-1) * total[total$Cell %in% cellIndex,]$CC, y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) + tmp_data <<- data.frame(x = total[total$Cell %in% cellIndex,]$CC, y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)),] titleLabel <<-"Voltage Profile" @@ -1062,12 +1006,12 @@ server <- function(input, output, session) { plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) if ("se" %in% colnames(tmp_data)) { arrows(tmp_data$x, tmp_data$y - tmp_data$se, tmp_data$x, tmp_data$y + tmp_data$se, col = tmp_data$color, length=0.05, angle=90, code=3) - legend("bottomright", legend = c(input$originalData, input$compareData), col = c(1,2), pch = 19) + #legend("bottomright", legend = c(input$originalData, input$compareData), col = c(1,2), pch = 19) } else { if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) } else { - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(1:length(input$cells)), title ="Cycle", ncol=2) + #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(1:length(input$cells)), title ="Cycle", ncol=2) } } } else if (input$plotStyle =="l") { @@ -1081,9 +1025,9 @@ server <- function(input, output, session) { } } if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells, if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + legend("topright", legend = c(sort(as.numeric(input$renderCycles)), if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) } else { - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells, if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(1:length(input$cells)), title ="Cycle", ncol=2) + legend("topright", legend = c(sort(as.numeric(input$renderCycles)), input$cells, if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(1:length(input$cells)), title ="Cycle", ncol=2) } } }, @@ -1164,7 +1108,7 @@ server <- function(input, output, session) { if (input$plotStyle =="o" | input$plotStyle =="p") { plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) } else if (input$plotStyle =="l") { newLine <- subset(tmp_data, tmp_data$color == 1 & tmp_data$symbol == 1) plot(newLine$x, newLine$y, type ="l", col = newLine$color, lty = newLine$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, lwd = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) @@ -1176,7 +1120,7 @@ server <- function(input, output, session) { } } - legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) } dev.off() diff --git a/ui.R b/ui.R index cbe0a05..4221496 100644 --- a/ui.R +++ b/ui.R @@ -18,7 +18,9 @@ ui <- shinyUI({ fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), - fluidRow(div(style = "color: red; font-size: 20pt;", "!!! Pay Attention. Controls Have Moved !!!"), align = "center"), + fluidRow("Analysis Mode", + switchInput("batchProcessing", onLabel = "Batch Processing", offLabel = "Single Analysis", handleWidth = "200px") + , align = "center", style = "margin: 0%"), # This first column is where most user inputs are, with the exception of the directory name column(4, align = "center", @@ -34,7 +36,7 @@ ui <- shinyUI({ fluidRow( fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), - actionButton("load", "Load"), + actionButton("load", "Load"), style = "border: 1px dashed black; margin: 5%; padding: 5%" ), @@ -63,9 +65,9 @@ ui <- shinyUI({ "Total Discharge Capacity","Average Voltage","Delta Voltage","Capacity Loss"), inline = FALSE), "Choose graphs to animate:", checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), - "Advanced Analysis", - #radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), - #helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), + # "Advanced Analysis", + # radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), + # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), style ="margin: 5%; border: 1px solid black; padding: 5%" ), ), @@ -73,7 +75,7 @@ ui <- shinyUI({ # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process column(4, align ="center", fluidRow( - selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), + selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "HighRate", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), strong("Files to be Analyzed*"), tags$br(), "Import all Arbin files of interest.", tags$br(), tags$br(), fileInput("files", NULL, multiple = TRUE), From 0b61eefb41751aae902ba50ab7ab5c6b6524c544 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Thu, 19 Aug 2021 15:16:48 -0400 Subject: [PATCH 10/19] restructuring --- info.R | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ modal.R | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 info.R create mode 100644 modal.R diff --git a/info.R b/info.R new file mode 100644 index 0000000..f9cfc7f --- /dev/null +++ b/info.R @@ -0,0 +1,85 @@ +graphInfoTable <- HTML(' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GraphX AxisY AxisPlot Frequency
Description
dQdV GraphVoltage (V)dQdV (Ah/V)per cycleThe differential capacity plot for each cycle
Voltage ProfileContinuous Capacity (mAh/g or Ah)Voltage (V)per cycleVoltage vs. capacity plot for each cycle. Units depend if the masses are specified.
Voltage vs. TimeTime (min)Voltage (V)per cycleThe voltage as a function of time, including all steps
Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per cellDischarge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.
Discharge Areal CapacityCycleDischarge Capacity (Ah/cm2)per cellDischarge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.
Total Discharge CapacityCycleDischarge Capacity (mAh/g or Ah)per analysisDischarge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.
Average VoltageCycleVoltage (V)per cellThe average voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The average voltage is then (Vcharge + Vdischarge)/2. Charge and discharge voltages are plotted alongside the average.
Delta VoltageCycleVoltage (V)per cellThe delta voltage vs capacity for each cycle. The charge voltage (Vcharge) and discharge voltage (Vdischarge) were calculated using the average value theorem. The delta voltage is then Vcharge - Vdischarge). Charge and discharge voltages are plotted alongside the average.
Capacity LossCycleCapacity (mAh/g or Ah)per cellThe discharge capacity minus the charge capacity for each cycle. Units depend if the masses are specified.
+ ') \ No newline at end of file diff --git a/modal.R b/modal.R new file mode 100644 index 0000000..2cabad1 --- /dev/null +++ b/modal.R @@ -0,0 +1,89 @@ +# Defines the modal in which the cell masses can be exported from Excel +graphModal <- modalDialog({ + fluidPage(style ="font-size:15pt;", + tags$head(tags$style(".modal-dialog{min-width:60%}")), + + fluidRow(align ="center", graphInfoTable), + + )}, title ="Graph Types", easyClose = TRUE) + +graphbuilder <- modalDialog({ + fluidPage( + useShinyjs(), + useShinyalert(), + + tags$head(tags$style(".modal-dialog{width:80%}")), + tags$head(tags$style(".modal-body{ min-height:1000px}")), + + sidebarLayout( + + sidebarPanel( + fluidRow( + headerPanel("Graph Options"), + ), + + fluidRow(style = "padding:5%; border: 1px solid black;", + strong("Basis for Plot Types"), tags$br(), + helpText("Between Analysis requires a second dataset to be imported"), tags$br(), + radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), + hidden(fileInput("compAnalysis", "Data to Compare")) + ), + + fluidRow(style ="padding:5%; margin:5%;", + radioButtons("typeGraph","Graph Type:", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" ), inline = FALSE), + radioButtons("plotStyle","Plot Style:", choiceNames = c("Point","Line","Both"), choiceValues = c("p","l","o"), inline = TRUE), + checkboxGroupInput("cells","Cell to Analyze:", choices = 1, inline = FALSE), + selectInput("renderCycles","Cycles of Interest:", choices = 1, multiple = TRUE), + ), + + fluidRow( + strong("Click to show coordinates:"), tags$br(), tags$br(), + "X: ", + textOutput("hoverCoordx", inline = TRUE), tags$br(), + "Y: ", + textOutput("hoverCoordy", inline = TRUE), + style ="border: 1px solid black; padding: 5%; margin: 5%;" + ), + + fluidRow( + textInput("fileName","Name of graph file:"), + actionButton("saveGraph","Save Graph", width = '100%', class = 'btn-primary'), + style ="border: 4px double black; padding: 5%; margin: 5%;" + ), + ), + + mainPanel( + fluidRow( + plotOutput("outputPlot", height ="800px", click = "plot_click"), + style = "padding: 5%;", + ), + fluidRow( + h3("Graph Formatting"), + helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), + column(2, + numericInput("xMin", "X Min", value = NULL), + sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column(2, + numericInput("xMax", "X Max", value = NULL), + sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column(2, + numericInput("yMin", "Y Min", value = NULL), + ), + column(2, + numericInput("yMax", "Y Max", value = NULL), + ), + column(4, + textInput("originalData", "Original Data Name", value = "Original Data"), + textInput("compareData", "Comparison Data Name", value = "Comparison Data"), + ), + style ="border: 1px dashed black; padding: 2%;", + ) + ) + ) + ) +}, size ="l", title ="Post-Processing Graph Builder") + From 54daee4dcad1888d7096eaba06248326892754d0 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Thu, 19 Aug 2021 15:17:15 -0400 Subject: [PATCH 11/19] batch processing --- global.R | 2 +- server.R | 831 +++++++++++++++++++++++++++---------------------------- ui.R | 3 +- 3 files changed, 404 insertions(+), 432 deletions(-) diff --git a/global.R b/global.R index 0905c18..551694e 100644 --- a/global.R +++ b/global.R @@ -12,7 +12,7 @@ # # ###### -options(warn = 2) +options(warn = 0) list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] diff --git a/server.R b/server.R index fd46e72..bfe3c8a 100644 --- a/server.R +++ b/server.R @@ -2,6 +2,9 @@ library(shiny) server <- function(input, output, session) { + utils <- c("modal.R") + lapply(utils, source) + # This sets the maximum file size Shiny will import, the default of 5Mb is not large enough to handle Arbin files options(shiny.maxRequestSize=1000*1024^2) @@ -13,6 +16,39 @@ server <- function(input, output, session) { batch.elements <- c("rerun", "load", "area", "whatGraph", "gGraphs", "gAnim", "graphBuilder") + deleteButtonCol <- function(df, id, ...) { + f <- function(i) { + as.character( + actionButton( + paste(id, i, sep = "_"), + label = icon("trash", lib = "glyphicon"), + onclick = 'Shiny.setInputValue(\"deletePressed\", this.id, {priority: "event"})' + ) + ) + } + + deleteCol <- unlist(lapply(seq_len(nrow(df)), f)) + + DT::datatable(cbind(df, delete = deleteCol),escape = FALSE, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5, 6, 7)))), + colnames = c("Group","Program","File","Sheet","Mass (g)","Filepath","Group Path","Limiting Electrode Area (cm^2)", "Delete"), rownames = FALSE) + + } + + parseDeleteEvent <- function(idstr) { + res <- as.integer(sub(".*_([0-9]+)", "\\1", idstr)) + if (! is.na(res)) { + res + } + } + + observeEvent(input$deletePressed, { + rowNum <- parseDeleteEvent(input$deletePressed) + + data <<- data[-rowNum,] + + output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + }) + observeEvent(input$batchProcessing, { if (input$batchProcessing) { lapply(batch.elements, hide) @@ -21,6 +57,17 @@ server <- function(input, output, session) { } }) + observeEvent(input$clearTable, { + shinyalert("Danger!", "Are you sure you wish to clear all cells?", + type ="warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", + callbackR = function(x) { + if (x) { + data <<- data[0,] + output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + } + }) + }) + export_to_origin <- function() { if (!("reticulate" %in% installed.packages()[, "Package"])) { install.packages("reticulate") @@ -37,21 +84,12 @@ server <- function(input, output, session) { system(paste(py_location$python, " rPyO.py ", filtered_location, sep='')) } - # Defines the modal in which the cell masses can be exported from Excel - graphModal <- modalDialog({ - fluidPage(style ="font-size:15pt;", - tags$head(tags$style(".modal-dialog{min-width:60%}")), - - fluidRow(align ="center", graphInfoTable), - - )}, title ="Graph Types", easyClose = TRUE) - # Ensures the files imported for analysis are Excel files observeEvent(input$files, { validFile <- FALSE for (file in 1:nrow(input$files)) { - if (file_ext(input$files[file, "name"]) == "xlsx" | file_ext(input$files[file,]) == "xls") { + if (file_ext(input$files[file, "name"]) == "xlsx" | file_ext(input$files[file, "name"]) == "xls") { validFile <- TRUE arbinCM <<- FALSE } else if (file_ext(file) == "csv") { @@ -73,7 +111,7 @@ server <- function(input, output, session) { #if (input$batchProcessing) { # append(dirLocation, chosenDir) #} else { - dirLocation <- chosenDir + dirLocation <<- chosenDir #} internal_folders <- list.dirs(path = dirLocation, recursive = FALSE) @@ -94,8 +132,7 @@ server <- function(input, output, session) { data <<- past$data } - output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(5)))), - colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) } } @@ -104,86 +141,6 @@ server <- function(input, output, session) { } }) - graphbuilder <- modalDialog({ - fluidPage( - useShinyjs(), - useShinyalert(), - - tags$head(tags$style(".modal-dialog{width:80%}")), - tags$head(tags$style(".modal-body{ min-height:1000px}")), - - sidebarLayout( - - sidebarPanel( - fluidRow( - headerPanel("Graph Options"), - ), - - fluidRow(style = "padding:5%; border: 1px solid black;", - strong("Basis for Plot Types"), tags$br(), - helpText("Between Analysis requires a second dataset to be imported"), tags$br(), - radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), - hidden(fileInput("compAnalysis", "Data to Compare")) - ), - - fluidRow(style ="padding:5%; margin:5%;", - radioButtons("typeGraph","Graph Type:", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", - "Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" ), inline = FALSE), - radioButtons("plotStyle","Plot Style:", choiceNames = c("Point","Line","Both"), choiceValues = c("p","l","o"), inline = TRUE), - checkboxGroupInput("cells","Cell to Analyze:", choices = 1, inline = FALSE), - selectInput("renderCycles","Cycles of Interest:", choices = 1, multiple = TRUE), - ), - - fluidRow( - strong("Click to show coordinates:"), tags$br(), tags$br(), - "X: ", - textOutput("hoverCoordx", inline = TRUE), tags$br(), - "Y: ", - textOutput("hoverCoordy", inline = TRUE), - style ="border: 1px solid black; padding: 5%; margin: 5%;" - ), - - fluidRow( - textInput("fileName","Name of graph file:"), - actionButton("saveGraph","Save Graph", width = '100%', class = 'btn-primary'), - style ="border: 4px double black; padding: 5%; margin: 5%;" - ), - ), - - mainPanel( - fluidRow( - plotOutput("outputPlot", height ="800px", click = "plot_click"), - style = "padding: 5%;", - ), - fluidRow( - h3("Graph Formatting"), - helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), - column(2, - numericInput("xMin", "X Min", value = NULL), - sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), - ), - column(2, - numericInput("xMax", "X Max", value = NULL), - sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), - ), - column(2, - numericInput("yMin", "Y Min", value = NULL), - ), - column(2, - numericInput("yMax", "Y Max", value = NULL), - ), - column(4, - textInput("originalData", "Original Data Name", value = "Original Data"), - textInput("compareData", "Comparison Data Name", value = "Comparison Data"), - ), - style ="border: 1px dashed black; padding: 2%;", - ) - ) - ) - ) - }, size ="l", title ="Post-Processing Graph Builder") - # Method for importing the previous R environment observeEvent(input$load, { if (is.null(input$rerun)) { @@ -205,8 +162,7 @@ server <- function(input, output, session) { total <<- total cycle_facts <<- cycle_facts - output$channels <- renderDataTable(data, editable = FALSE, options=list(columnDefs = list(list(visible=FALSE, targets=c(4)))), - colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) enable("graphBuilder") } else { @@ -215,6 +171,34 @@ server <- function(input, output, session) { } }) + observeEvent(input$whatGraph, { + showModal(graphModal) + }) + + # Data validation the masses imported from Excel, if valid they are placed into the datatable + observeEvent(input$excelImport, { + if (length(names(data)) <= 1) { + shinyalert("Uh oh!","You need to import cells first!","error") + removeModal() + } else { + tryCatch({ + masses <- lapply(strsplit(strRep(input$masses, "\n", ","), ",", fixed = TRUE), as.double) + names(masses)[names(masses) =="V1"] <- "Mass" + data$Mass <<- masses[[1]] + }, error = function(cond) { + print(cond) + shinyalert("Something isn't right...","The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.","error") + removeModal() + }, finally = { + replaceData(proxy, data) + + renderDataTable(deleteButtonCol(data, 'delete_button')) + + removeModal() + }) + } + }) + # After the validation of the Arbin files, they macros (file name, and sheets) are taken and rendered in to a datatable renderTable <- function() { @@ -230,8 +214,8 @@ server <- function(input, output, session) { } else { for (i in 1:nrow(files)) { sheets <- excel_sheets(files[i, 4]) - file_sheet <- rbind(file_sheet, data.frame(group = basename(dirLocation), name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), - datapath = rep(files[["datapath"]][i], length(sheets)), area = rep(input$area, length(sheets)))) + file_sheet <- rbind(file_sheet, data.frame(group = basename(dirLocation), program = rep(input$dirName, length(sheets)), name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), + datapath = rep(files[["datapath"]][i], length(sheets)), grouppath = rep(dirLocation, length(sheets)), area = rep(input$area, length(sheets)))) } raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) @@ -243,49 +227,24 @@ server <- function(input, output, session) { data$datapath <<- raw_data$datapath } else { if (input$batchProcessing) { - data <<- rbind(data, raw_data) + tryCatch( { + data <<- rbind(data, raw_data) + }, error = function(cond) { + data <<- raw_data + }) } else { data <<- raw_data } } if (arbinCM) { - output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), + output$channels <- renderDataTable(data, editable = TRUE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), colnames = c("File", "Sheet", "Filepath")) } else { - output$channels <- renderDataTable(data, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5)))), - colnames = c("Group", "File","Sheet","Mass (g)","Filepath","Limiting Electrode Area (cm^2)")) + output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) } } - observeEvent(input$whatGraph, { - showModal(graphModal) - }) - - # Data validation the masses imported from Excel, if valid they are placed into the datatable - observeEvent(input$excelImport, { - if (length(names(data)) <= 1) { - shinyalert("Uh oh!","You need to import cells first!","error") - removeModal() - } else { - tryCatch({ - masses <- lapply(strsplit(strRep(input$masses, "\n", ","), ",", fixed = TRUE), as.double) - names(masses)[names(masses) =="V1"] <- "Mass" - data$Mass <<- masses[[1]] - }, error = function(cond) { - print(cond) - shinyalert("Something isn't right...","The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.","error") - removeModal() - }, finally = { - replaceData(proxy, data) - - renderDataTable(data) - - removeModal() - }) - } - }) - # After some data validation, the main analysis is run on click of the"Run Analysis" button observeEvent(input$submit, { if (length(names(data)) <= 1) { @@ -311,358 +270,367 @@ server <- function(input, output, session) { # This function responsible for analysis of the runscript <- function() { - # Sets up a progress bar in which to estimate how long the execution of the code will take - progress <- Progress$new(session, min = 0, max = nrow(data)) - progress$set(message ="Plugging and chugging...\n", detail ="Starting up...") - - # Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) - while (dev.cur() != 1) { - dev.off() - } - - # Resets the variables for the graph builder so new results are concatenated to old ones - numCycles <<- data.frame() - dQdVData <<- data.frame() - total <<- data.frame() - cycle_facts <<- data.frame() + groupList <- unique(data[["group"]]) - # Disable all input fields to prevent errors occurring from changing values - disable("files") - disable("lowV") - disable("highV") - disable("dirLocation") - disable("submit") - disable("excelImport") - disable("gGraphs") - disable("peakFit") - disable("area") - disable("perActive") - disable("capActive") - - # Creates the directory in which all data will be stored - dir.create(paste(dirLocation, input$dirName, sep = "/")) - - # Update the status once all set-up functions are complete - progress$set(detail ="Starting first cell...") - - # ###### - # - # The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. - # - # ###### - for (row in 1:nrow(data)) { + for (group in groupList) { - # ###### - # - # This is where all code that should be executed on a"per cell" basis, to prepare for analysis - # - # ###### + dataSubset <- data[data[["group"]] == group,] + dirLocation <- dataSubset[["grouppath"]][1] + programName <- dataSubset[["program"]][1] + + # Sets up a progress bar in which to estimate how long the execution of the code will take + progress <- Progress$new(session, min = 0, max = nrow(dataSubset)) + progress$set(message = paste(group, ": Plugging and chugging...\n"), detail ="Starting up...") - # Import the excel sheet corresponding to cell of interest - if (arbinCM) { - tmp_excel <- read.csv(toString(data$datapath[row])) - names(tmp_excel) <- c("Index", "Test_Time(s)", "Date_Time", "Step_Time(s)", "Step_Index", "Cycle_Index", "Current(A)", "Voltage(V)", "Charge_Capacity(Ah)", "Discharge_Capacity(Ah)", "Charge_Energy(Wh)", "Discharge_Energy(Wh)") - } else { - tmp_excel <- read_excel(toString(data$datapath[row]), toString(data$sheet[row])) + # Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) + while (dev.cur() != 1) { + dev.off() } - # Create an nested directory for all the data and, if applicable, then further folders for graphs of interest - dir.create(paste(dirLocation, "/", input$dirName, data$sheet[row], sep ="/")) - if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) - if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) - if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation, input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) + # Resets the variables for the graph builder so new results are concatenated to old ones + numCycles <<- data.frame() + dQdVdata <<- data.frame() + total <<- data.frame() + cycle_facts <<- data.frame() - # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis - if (sum(data$Mass) != 0) { - ylabel <-"Capacity (mAh/g)" - - tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / data$Mass[row])) - tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)`* (1000 / data$Mass[row])) - - tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c - tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 - } else { - ylabel <-"Capacity (Ah)" - - tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` - tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 - } - tmp_excel$Cell <- row - tmp_excel$CE[is.infinite(tmp_excel$CE)|is.nan(tmp_excel$CE)|tmp_excel$CE > 200] <- 0; + # Disable all input fields to prevent errors occurring from changing values + disable("files") + disable("lowV") + disable("highV") + disable("dirLocation") + disable("submit") + disable("excelImport") + disable("gGraphs") + disable("peakFit") + disable("area") + disable("perActive") + disable("capActive") + + # Creates the directory in which all dataSubset will be stored + dir.create(paste(dirLocation, programName, sep = "/")) + + # Update the status once all set-up functions are complete + progress$set(detail ="Starting first cell...") # ###### # - # This loop iterates through each cycle of the cell. + # The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. # # ###### - cycles <- split(tmp_excel, tmp_excel$Cycle_Index) - prev_c <- 0 - lastCC <- 0 - ch_dch <- TRUE - durations <- vector(length = 4) - caps <- vector(length = 4) - prev <- TRUE - dchV <- 0 - chV <- 0 - i <- 1 - for (cycle in cycles) { + for (row in 1:nrow(dataSubset)) { # ###### # - # Within each cycle, take out the individual steps. These include the charge, discharge, and others. + # This is where all code that should be executed on a"per cell" basis, to prepare for analysis # # ###### - progress$set(detail = paste("Analyzing cell", row,", cycle", i)) + # Import the excel sheet corresponding to cell of interest + if (arbinCM) { + tmp_excel <- read.csv(toString(dataSubset$dataSubsetpath[row])) + names(tmp_excel) <- c("Index", "Test_Time(s)", "Date_Time", "Step_Time(s)", "Step_Index", "Cycle_Index", "Current(A)", "Voltage(V)", "Charge_Capacity(Ah)", "Discharge_Capacity(Ah)", "Charge_Energy(Wh)", "Discharge_Energy(Wh)") + } else { + tmp_excel <- read_excel(toString(dataSubset$datapath[row]), toString(dataSubset$sheet[row])) + } - steps <- split(cycle, cycle$Step_Index) - n <- 1 + # Create an nested directory for all the dataSubset and, if applicable, then further folders for graphs of interest + dir.create(paste(dirLocation, "/", programName, dataSubset$sheet[row], sep ="/")) + if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"dQdV Plots", sep ="/")) + if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"Voltage Profiles", sep ="/")) + if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"Voltage v Time", sep ="/")) - for (step in steps) { + # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis + if (sum(dataSubset$Mass) != 0) { + ylabel <-"Capacity (mAh/g)" + + tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / dataSubset$Mass[row])) + tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)`* (1000 / dataSubset$Mass[row])) + + tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c + tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 + } else { + ylabel <-"Capacity (Ah)" + + tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` + tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 + } + tmp_excel$Cell <- row + tmp_excel$CE[is.infinite(tmp_excel$CE)|is.nan(tmp_excel$CE)|tmp_excel$CE > 200] <- 0; + + # ###### + # + # This loop iterates through each cycle of the cell. + # + # ###### + cycles <- split(tmp_excel, tmp_excel$Cycle_Index) + prev_c <- 0 + lastCC <- 0 + ch_dch <- TRUE + durations <- vector(length = 4) + caps <- vector(length = 4) + prev <- TRUE + dchV <- 0 + chV <- 0 + i <- 1 + for (cycle in cycles) { # ###### # - # Isolation of the charge and discharge cycles. The algorithm is as follows: - # - # If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines - # if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). + # Within each cycle, take out the individual steps. These include the charge, discharge, and others. # # ###### - if (abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) > 0.5) { + + progress$set(detail = paste("Analyzing cell", row,", cycle", i)) + + steps <- split(cycle, cycle$Step_Index) + n <- 1 + + for (step in steps) { # ###### # - # All code that should be executed for every charge/discharge cycles should be written here. + # Isolation of the charge and discharge cycles. The algorithm is as follows: + # + # If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines + # if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). # # ###### - lastCC <- n - if (step$'Current(A)'[[1]] > 0) { - chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) - dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) + if (abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) > 0.5) { - durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] - ch_dch <- TRUE - } else { - dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`,1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) - dQDdV <- diff(step$`Discharge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - - durations[3] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] - ch_dch <- FALSE - if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) - prev_c = step$`Current(A)`[[1]] + # ###### + # + # All code that should be executed for every charge/discharge cycles should be written here. + # + # ###### + lastCC <- n + if (step$'Current(A)'[[1]] > 0) { + chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) + dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) + dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) + + durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] + caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] + ch_dch <- TRUE } else { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) + dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`,1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) + dQDdV <- diff(step$`Discharge_Capacity(Ah)`)/diff(step$`Voltage(V)`) + + durations[3] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] + caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] + ch_dch <- FALSE + if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { + if (!arbinCM) dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) + prev_c = step$`Current(A)`[[1]] + } else { + if (!arbinCM) dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) + } + } + } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { + if (step$'Current(A)'[[1]] > 0) { + durations[2] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] + caps[2] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] + } else { + durations[4] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] + caps[4] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] } } - } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { - if (step$'Current(A)'[[1]] > 0) { - durations[2] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[2] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] - } else { - durations[4] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[4] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] - } + ch_dch <- FALSE + n <- n + 1 } - ch_dch <- FALSE - n <- n + 1 + + # ###### + # + # Code meant to be run on dataSubset"per cycle" should be written here + # + # ###### + + #dQdVdata <<- dQdVdata[is.finite(dQdVdata$voltage),] + #dQdVdata <<- dQdVdata[is.finite(dQdVdata$dQdV),] + + if (sum(dataSubset$Mass) != 0) { + DCap <- tail(cycle$Q.d, 1) + CCap <- tail(cycle$Q.c, 1) + } else { + DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) + CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) + } + + timeCVFracCh <- durations[1] / (durations[1] + durations[2]) + timeCVFracDch <- durations[3] / (durations[3] + durations[4]) + capCVFracCh <- caps[1] / (caps[1] + caps[2]) + capCVFracDch <- caps[3] / (caps[3] + caps[4]) + + if (max(tmp_excel$voltage) > 2) { + CE <- DCap / CCap + } else { + CE <- CCap / DCap + } + + # Record charge and discharge voltage, then calculate the delta and average voltage + cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, + dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = CE * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, + cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, + capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) + + i <- i + 1 } # ###### # - # Code meant to be run on data"per cycle" should be written here + # Code meant to be run on dataSubset"per cell" should be written here # # ###### - #dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] - #dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] + cell_dataSubset <- cycle_facts[cycle_facts$cell == row,] - if (sum(data$Mass) != 0) { - DCap <- tail(cycle$Q.d, 1) - CCap <- tail(cycle$Q.c, 1) - } else { - DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) - CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) + # Discharge capacity plotting, with coulombic efficiency being plotted alongside + if (is.element("Discharge Capacity", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Discharge Capacity Plot.png", sep ="")) + eol <- cell_dataSubset$`DCap`[[1]] * 0.8 + plot(cell_dataSubset$cycle, cell_dataSubset$DCap, type ="p", main=paste("Discharge Capacity for", programName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) + abline(h=eol, lty ="dotted") + par(new = T) + plot(cell_dataSubset$cycle, cell_dataSubset$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) + mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") + axis(side = 4, col ="red", col.axis = "red") + dev.off() + } + + # Discharge areal capacity plotting, with coulombic efficiency being plotted alongside + if (is.element("Discharge Areal Capacity", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) + new_par <- old_par <- par("mar") + new_par[4] <- old_par[2] + par(mar = new_par) + eol <- ((cell_dataSubset$DCap[[1]] * 1000) / dataSubset$area[row]) * 0.8 + plot(cell_dataSubset$cycle, ((cell_dataSubset$DCap * 1000) / dataSubset$area[row]), type ="p", main=paste("Discharge Areal Capacity for", programName), xlab=NA, ylab="Discharge Capacity (mAh/cm^2)", mai = c(1,1,1,2)) + abline(h=eol, lty ="dotted") + par(new = T) + plot(cell_dataSubset$cycle, cell_dataSubset$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) + mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") + axis(side = 4, col ="red", col.axis = "red") + dev.off() } - timeCVFracCh <- durations[1] / (durations[1] + durations[2]) - timeCVFracDch <- durations[3] / (durations[3] + durations[4]) - capCVFracCh <- caps[1] / (caps[1] + caps[2]) - capCVFracDch <- caps[3] / (caps[3] + caps[4]) - if (max(tmp_excel$voltage) > 2) { - CE <- DCap / CCap - } else { - CE <- CCap / DCap + # Average voltage plotting + if (is.element("Average Voltage", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Average Voltage Plot.png", sep ="")) + plot(cell_dataSubset$cycle, cell_dataSubset$chV, col="blue", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_dataSubset[,2:4]), max(cell_dataSubset[,2:4]))) + points(cell_dataSubset$cycle, cell_dataSubset$dchV, col="red", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)") + points(cell_dataSubset$cycle, cell_dataSubset$avgV, col="black", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)") + legend("bottomright", c("Charge Voltage","Discharge Voltage","Average Voltage"), col=c("blue","red","black"), pch=19) + dev.off() } - # Record charge and discharge voltage, then calculate the delta and average voltage - cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, - dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = CE * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, - cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, - capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) + # Delta voltage plotting + if (is.element("Delta Voltage", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Delta Voltage Plot.png", sep ="")) + plot(cell_dataSubset$cycle, cell_dataSubset$dV, main=paste("Delta Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) + dev.off() + } + + # Capacity Loss plotting + if (is.element("Capacity Loss", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Capacity Loss Plot.png", sep ="")) + plot(cell_dataSubset$cycle, cell_dataSubset$lostCap, main=paste("Capacity Loss Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_dataSubset$lostCap) + (2* sd(cell_dataSubset$lostCap)), mean(cell_dataSubset$lostCap) - (1.5* sd(cell_dataSubset$lostCap)))) + abline(h=median(cell_dataSubset$lostCap), lty="dotted") + dev.off() + } - i <- i + 1 + if (is.element("dQdV Plots", input$gAnim)) { + dQdVplot <- function(){ + tmp_dataSubset <- dQdVdata[dQdVdata$cell == row,] + first_cycle <- dQdVdata[dQdVdata$cell == row & dQdVdata$cycle == 2,] + dataSubsetlist <- split(tmp_dataSubset, tmp_dataSubset$cycle) + lapply(dataSubsetlist, function(plotdataSubset){ + p <- plot(plotdataSubset$voltage, plotdataSubset$dQdV, main=paste("dQdV Plot for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", + xlim = c(min(tmp_dataSubset$voltage), max(tmp_dataSubset$voltage)), ylim = c(min(tmp_dataSubset$dQdV), max(tmp_dataSubset$dQdV))) + + points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) + }) + } + save_gif(dQdVplot(), paste(dirLocation, programName, dataSubset$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) + } + + if (is.element("Voltage Profiles", input$gAnim)) { + vpPlot <- function(){ + first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2,] + dataSubsetlist <- split(tmp_excel, tmp_excel$`Cycle_Index`) + lapply(dataSubsetlist, function(plotdataSubset){ + p <- plot(plotdataSubset$CC, plotdataSubset$`Voltage(V)`, main=paste("Voltage Profile for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$`Cycle_Index`[[1]]), xlab=ylabel, ylab= "Voltage (V)", + xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`))) + + points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) + }) + } + save_gif(vpPlot(), paste(dirLocation, programName, dataSubset$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) + } + + # Save all dataSubset within the cell's directory + write.csv(tmp_excel, file = paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row],".csv", sep ="")) + + # Append summation dataSubset to the larger dataSubsetsets to be worked with later + final <- rbind(final, tmp_excel) + numCycles <<- rbind(numCycles, data.frame(sheet=dataSubset$sheet[row], cycles=nrow(cell_dataSubset))) + + # Update progress bar + progress$set(value = row, detail = paste("Finished", row," of", nrow(dataSubset)," cells.")) } # ###### # - # Code meant to be run on data"per cell" should be written here + # Code meant to be run on all dataSubset of all cells should be written here. # # ###### - cell_data <- cycle_facts[cycle_facts$cell == row,] - - # Discharge capacity plotting, with coulombic efficiency being plotted alongside - if (is.element("Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Capacity Plot.png", sep ="")) - eol <- cell_data$`DCap`[[1]] * 0.8 - plot(cell_data$cycle, cell_data$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) - abline(h=eol, lty ="dotted") - par(new = T) - plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") - axis(side = 4, col ="red", col.axis = "red") - dev.off() - } - - # Discharge areal capacity plotting, with coulombic efficiency being plotted alongside - if (is.element("Discharge Areal Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) - new_par <- old_par <- par("mar") - new_par[4] <- old_par[2] - par(mar = new_par) - eol <- ((cell_data$DCap[[1]] * 1000) / data$area[row]) * 0.8 - plot(cell_data$cycle, ((cell_data$DCap * 1000) / data$area[row]), type ="p", main=paste("Discharge Areal Capacity for", input$dirName), xlab=NA, ylab="Discharge Capacity (mAh/cm^2)", mai = c(1,1,1,2)) - abline(h=eol, lty ="dotted") - par(new = T) - plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") - axis(side = 4, col ="red", col.axis = "red") - dev.off() - } + # With iterations complete, final calculations are being worked + progress$set(detail ="Wrapping up...") + # Get the last status of each cycle for each cell (namely capacity) + DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) + CE <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(mean) + capSEs <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) + ceSEs <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(se) + stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) - # Average voltage plotting - if (is.element("Average Voltage", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Average Voltage Plot.png", sep ="")) - plot(cell_data$cycle, cell_data$chV, col="blue", main=paste("Average Voltage Plot for", input$dirname, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_data[,2:4]), max(cell_data[,2:4]))) - points(cell_data$cycle, cell_data$dchV, col="red", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") - points(cell_data$cycle, cell_data$avgV, col="black", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") - legend("bottomright", c("Charge Voltage","Discharge Voltage","Average Voltage"), col=c("blue","red","black"), pch=19) - dev.off() - } - - # Delta voltage plotting - if (is.element("Delta Voltage", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Delta Voltage Plot.png", sep ="")) - plot(cell_data$cycle, cell_data$dV, main=paste("Delta Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) - dev.off() - } + # Send all the dataSubset to a global variable to be used elsewhere + total <<- final - # Capacity Loss plotting - if (is.element("Capacity Loss", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Capacity Loss Plot.png", sep ="")) - plot(cell_data$cycle, cell_data$lostCap, main=paste("Capacity Loss Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_data$lostCap) + (2* sd(cell_data$lostCap)), mean(cell_data$lostCap) - (1.5* sd(cell_data$lostCap)))) - abline(h=median(cell_data$lostCap), lty="dotted") - dev.off() - } - - if (is.element("dQdV Plots", input$gAnim)) { - dQdVplot <- function(){ - tmp_data <- dQdVData[dQdVData$cell == row,] - first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2,] - datalist <- split(tmp_data, tmp_data$cycle) - lapply(datalist, function(plotData){ - p <- plot(plotData$voltage, plotData$dQdV, main=paste("dQdV Plot for", input$dirName, data$sheet[row], "Cycle", plotData$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", - xlim = c(min(tmp_data$voltage), max(tmp_data$voltage)), ylim = c(min(tmp_data$dQdV), max(tmp_data$dQdV))) + - points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) - }) + tryCatch({ + # Total dishcharge capacity plotting + if (is.element("Total Discharge Capacity", input$gGraphs)) { + png(paste(dirLocation, "/", programName,"/", "Total Discharge Capacity Plot.png", sep ="")) + eol <- max(stats$DCap) * 0.8 + plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", programName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) + arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) + abline(h=eol, lty ="dotted") + par(new = T) + plot(stats$cycle, stats$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) + arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length=0.05, angle=90, code=3, col ="red") + axis(side = 4, col ="red") + mtext(side = 4, line = 2,"Coulombic Efficiency (%)") + dev.off() } - save_gif(dQdVplot(), paste(dirLocation, input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) - } + }, error = function(cond) { + print(cond) + }) - if (is.element("Voltage Profiles", input$gAnim)) { - vpPlot <- function(){ - first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2,] - datalist <- split(tmp_excel, tmp_excel$`Cycle_Index`) - lapply(datalist, function(plotData){ - p <- plot(plotData$CC, plotData$`Voltage(V)`, main=paste("Voltage Profile for", input$dirName, data$sheet[row], "Cycle", plotData$`Cycle_Index`[[1]]), xlab=ylabel, ylab= "Voltage (V)", - xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`))) + - points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) - }) - } - save_gif(vpPlot(), paste(dirLocation, input$dirName, data$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) - } + # Save total dataSubset and stats + write.csv(stats, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Summary.csv", sep ="")) + if (!arbinCM) write.csv(dQdVdata, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," dQdV data.csv", sep ="")) + write.csv(cycle_facts, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Cycle Facts.csv", sep ="")) - # Save all data within the cell's directory - write.csv(tmp_excel, file = paste(dirLocation, "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) + # If a histor directory does not exist, create it. Save all the dataSubset revelant to plotting to a RdataSubset file. + if (!dir.exists(paste(dirLocation, "history", sep = "/"))) { + dir.create(paste(dirLocation, "history", sep = "/")) + } - # Append summation data to the larger datasets to be worked with later - final <- rbind(final, tmp_excel) - numCycles <<- rbind(numCycles, data.frame(sheet=data$sheet[row], cycles=nrow(cell_data))) + dirName <<- programName - # Update progress bar - progress$set(value = row, detail = paste("Finished", row," of", nrow(data)," cells.")) + save(dirLocation, dirName, dataSubset, dQdVdata, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".RdataSubset", sep = "")) } - # ###### - # - # Code meant to be run on all data of all cells should be written here. - # - # ###### - - # With iterations complete, final calculations are being worked - progress$set(detail ="Wrapping up...") - - # Get the last status of each cycle for each cell (namely capacity) - DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) - CE <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(mean) - capSEs <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) - ceSEs <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(se) - stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) - - # Send all the data to a global variable to be used elsewhere - total <<- final - - tryCatch({ - # Total dishcharge capacity plotting - if (is.element("Total Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", input$dirName,"/", "Total Discharge Capacity Plot.png", sep ="")) - eol <- max(stats$DCap) * 0.8 - plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) - arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) - abline(h=eol, lty ="dotted") - par(new = T) - plot(stats$cycle, stats$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length=0.05, angle=90, code=3, col ="red") - axis(side = 4, col ="red") - mtext(side = 4, line = 2,"Coulombic Efficiency (%)") - dev.off() - } - }, error = function(cond) { - print(cond) - }) - - # Save total data and stats - write.csv(stats, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," Summary.csv", sep ="")) - if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," dQdV Data.csv", sep ="")) - write.csv(cycle_facts, file = paste(dirLocation, "/", input$dirName,"/", basename(dirLocation)," Cycle Facts.csv", sep ="")) - - # If a histor directory does not exist, create it. Save all the data revelant to plotting to a RData file. - if (!dir.exists(paste(dirLocation, "history", sep = "/"))) { - dir.create(paste(dirLocation, "history", sep = "/")) - } - - dirName <<- input$dirName - - save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", input$dirName, ".RData", sep = "")) - # Modal for completed analysis - shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation, "/", input$dirName, sep = ""), + shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation, "/", programName, sep = ""), type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", callbackR = function(x) { if (x) { @@ -688,23 +656,12 @@ server <- function(input, output, session) { enable("capActive") enable("graphBuilder") - # Close progress bar - progress$close() - } - - # Enable/Disable input field based on desired grph selection - observeEvent(input$gGraphs, { - disable("area") - disable("perActive") - disable("capActive") - choices <- c("dQdV Graphs","Voltage Profiles","Voltage vs. Time","Discharge Capacity","Discharge Areal Capacity", - "Total Discharge Capacity","Average Voltage","Delta Voltage") - - if (is.element("Discharge Areal Capacity", input$gGraphs)) { - enable("area") + for (num in 1:size(groupList)[2]) { + # Close progress bar + progress$close() } - }) + } # ###### # @@ -1042,6 +999,20 @@ server <- function(input, output, session) { }) }, res = 125) + # Enable/Disable input field based on desired grph selection + observeEvent(input$gGraphs, { + disable("area") + disable("perActive") + disable("capActive") + + choices <- c("dQdV Graphs","Voltage Profiles","Voltage vs. Time","Discharge Capacity","Discharge Areal Capacity", + "Total Discharge Capacity","Average Voltage","Delta Voltage") + + if (is.element("Discharge Areal Capacity", input$gGraphs)) { + enable("area") + } + }) + # Method for handling changes in cell selection observeEvent(input$cells, { tmp_cycles <<- input$renderCycles diff --git a/ui.R b/ui.R index 4221496..66ee8cd 100644 --- a/ui.R +++ b/ui.R @@ -3,7 +3,7 @@ library(shiny) ui <- shinyUI({ fluidPage( - # Utilizing javascript within Shiny allows for addd features such as enable/disable of inputs + # Utilizing javascript within Shiny allows for added features such as enable/disable of inputs useShinyjs(), # Shinyalert is a package that makes interactive"pop-ups" (modals) easy to generate @@ -80,6 +80,7 @@ ui <- shinyUI({ "Import all Arbin files of interest.", tags$br(), tags$br(), fileInput("files", NULL, multiple = TRUE), actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width:80%; height:100px; margin:5%; font-size:100%"), + actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width:60%; margin:5%; font-size:100%"), style = "border: 4px double black; padding: 5%; margin:5%" ), From 943acdcb6505e3c1c8c922cae81f13f645dd6b89 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Thu, 19 Aug 2021 16:12:20 -0400 Subject: [PATCH 12/19] style editor update --- .stylelintignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .stylelintignore diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000..40db850 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1 @@ +info.R From 1d690d24e591eff3cdfa78c9181d5a1d502cfc14 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Thu, 19 Aug 2021 16:13:33 -0400 Subject: [PATCH 13/19] style fixes --- global.R | 2 +- server.R | 55 ++++++++++++++++++++++++++----------------------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/global.R b/global.R index 551694e..aa67795 100644 --- a/global.R +++ b/global.R @@ -12,7 +12,7 @@ # # ###### -options(warn = 0) +# options(warn = 0) list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] diff --git a/server.R b/server.R index bfe3c8a..c59261e 100644 --- a/server.R +++ b/server.R @@ -29,9 +29,12 @@ server <- function(input, output, session) { deleteCol <- unlist(lapply(seq_len(nrow(df)), f)) - DT::datatable(cbind(df, delete = deleteCol),escape = FALSE, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5, 6, 7)))), - colnames = c("Group","Program","File","Sheet","Mass (g)","Filepath","Group Path","Limiting Electrode Area (cm^2)", "Delete"), rownames = FALSE) + cbind(data, delete = deleteCol) + } + generateDataTable <- function(df) { + DT::datatable(df,escape = FALSE, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5, 6, 7)))), + colnames = c("Group","Program","File","Sheet","Mass (g)","Filepath","Group Path","Limiting Electrode Area (cm^2)", "Delete"), rownames = FALSE) } parseDeleteEvent <- function(idstr) { @@ -46,7 +49,7 @@ server <- function(input, output, session) { data <<- data[-rowNum,] - output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) }) observeEvent(input$batchProcessing, { @@ -58,12 +61,12 @@ server <- function(input, output, session) { }) observeEvent(input$clearTable, { - shinyalert("Danger!", "Are you sure you wish to clear all cells?", + shinyalert("Warning!", "Are you sure you wish to clear all cells?", type ="warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", callbackR = function(x) { if (x) { data <<- data[0,] - output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) } }) }) @@ -132,7 +135,7 @@ server <- function(input, output, session) { data <<- past$data } - output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) } } @@ -148,21 +151,20 @@ server <- function(input, output, session) { } else { load(input$rerun$datapath[[1]]) - validFile <- FALSE - - if (file_ext(input$rerun$datapath) =="RData") { + + if (file_ext(input$rerun$datapath) == "RData") { validFile <- TRUE } if (validFile) { - data <<- filter(data, grepl('Channel', sheet)) - dirLocation(dirLocation) + data <<- dataSubset + dirLocation <<- dirLocation numCycles <<- numCycles dQdVData <<- dQdVData total <<- total cycle_facts <<- cycle_facts - output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) enable("graphBuilder") } else { @@ -183,18 +185,13 @@ server <- function(input, output, session) { } else { tryCatch({ masses <- lapply(strsplit(strRep(input$masses, "\n", ","), ",", fixed = TRUE), as.double) - names(masses)[names(masses) =="V1"] <- "Mass" data$Mass <<- masses[[1]] }, error = function(cond) { print(cond) shinyalert("Something isn't right...","The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.","error") removeModal() }, finally = { - replaceData(proxy, data) - - renderDataTable(deleteButtonCol(data, 'delete_button')) - - removeModal() + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) }) } }) @@ -241,7 +238,7 @@ server <- function(input, output, session) { output$channels <- renderDataTable(data, editable = TRUE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), colnames = c("File", "Sheet", "Filepath")) } else { - output$channels <- renderDataTable(deleteButtonCol(data, 'delete_button')) + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) } } @@ -289,7 +286,7 @@ server <- function(input, output, session) { # Resets the variables for the graph builder so new results are concatenated to old ones numCycles <<- data.frame() - dQdVdata <<- data.frame() + dQdVData <<- data.frame() total <<- data.frame() cycle_facts <<- data.frame() @@ -406,7 +403,7 @@ server <- function(input, output, session) { if (step$'Current(A)'[[1]] > 0) { chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) + dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] @@ -419,10 +416,10 @@ server <- function(input, output, session) { caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] ch_dch <- FALSE if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { - if (!arbinCM) dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) prev_c = step$`Current(A)`[[1]] } else { - if (!arbinCM) dQdVdata <<- rbind(dQdVdata, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) } } } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { @@ -444,8 +441,8 @@ server <- function(input, output, session) { # # ###### - #dQdVdata <<- dQdVdata[is.finite(dQdVdata$voltage),] - #dQdVdata <<- dQdVdata[is.finite(dQdVdata$dQdV),] + dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] + dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] if (sum(dataSubset$Mass) != 0) { DCap <- tail(cycle$Q.d, 1) @@ -540,8 +537,8 @@ server <- function(input, output, session) { if (is.element("dQdV Plots", input$gAnim)) { dQdVplot <- function(){ - tmp_dataSubset <- dQdVdata[dQdVdata$cell == row,] - first_cycle <- dQdVdata[dQdVdata$cell == row & dQdVdata$cycle == 2,] + tmp_dataSubset <- dQdVData[dQdVData$cell == row,] + first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2,] dataSubsetlist <- split(tmp_dataSubset, tmp_dataSubset$cycle) lapply(dataSubsetlist, function(plotdataSubset){ p <- plot(plotdataSubset$voltage, plotdataSubset$dQdV, main=paste("dQdV Plot for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", @@ -616,7 +613,7 @@ server <- function(input, output, session) { # Save total dataSubset and stats write.csv(stats, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Summary.csv", sep ="")) - if (!arbinCM) write.csv(dQdVdata, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," dQdV data.csv", sep ="")) + if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," dQdV data.csv", sep ="")) write.csv(cycle_facts, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Cycle Facts.csv", sep ="")) # If a histor directory does not exist, create it. Save all the dataSubset revelant to plotting to a RdataSubset file. @@ -626,7 +623,7 @@ server <- function(input, output, session) { dirName <<- programName - save(dirLocation, dirName, dataSubset, dQdVdata, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".RdataSubset", sep = "")) + save(dirLocation, dirName, dataSubset, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".Rdata", sep = "")) } # Modal for completed analysis From 24da4bd1c40edb8d93e68538921ca61da6e2748b Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Thu, 19 Aug 2021 16:44:54 -0400 Subject: [PATCH 14/19] CC in dQdV datafile --- info.R | 27 ++++++++++++++------------- server.R | 6 +++--- ui.R | 36 ++++++++++++++++++------------------ 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/info.R b/info.R index f9cfc7f..1f477df 100644 --- a/info.R +++ b/info.R @@ -1,21 +1,22 @@ + graphInfoTable <- HTML(' - + @@ -44,21 +45,21 @@ graphInfoTable <- HTML(' - + - + - + diff --git a/server.R b/server.R index c59261e..81b136e 100644 --- a/server.R +++ b/server.R @@ -403,7 +403,7 @@ server <- function(input, output, session) { if (step$'Current(A)'[[1]] > 0) { chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) + dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1), CC=step[['CC']][1:nrow(step)])) durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] @@ -416,10 +416,10 @@ server <- function(input, output, session) { caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] ch_dch <- FALSE if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1), CC=step[['CC']][1:nrow(step)])) prev_c = step$`Current(A)`[[1]] } else { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1), CC=step[['CC']][1:nrow(step)])) } } } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { diff --git a/ui.R b/ui.R index 66ee8cd..bd82d92 100644 --- a/ui.R +++ b/ui.R @@ -20,7 +20,7 @@ ui <- shinyUI({ fluidRow("Analysis Mode", switchInput("batchProcessing", onLabel = "Batch Processing", offLabel = "Single Analysis", handleWidth = "200px") - , align = "center", style = "margin: 0%"), + , align = "center", style = "margin: 0%"), # This first column is where most user inputs are, with the exception of the directory name column(4, align = "center", @@ -28,16 +28,16 @@ ui <- shinyUI({ # This generates the optional block in which the user can import a previous R environment fluidRow( strong("Start Here"), tags$br(), - "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), - actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width:80%; margin:5%; font-size:100%"), tags$br(), + "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), + actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width: 80%; margin: 5%; font-size: 100%"), tags$br(), helpText("The analysis will create a folder within the selected folder."), - style = "border: 1px solid black; padding: 5%; margin:5%" + style = "border: 1px solid black; padding: 5%; margin: 5%" ), fluidRow( - fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), + fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), actionButton("load", "Load"), - style = "border: 1px dashed black; margin: 5%; padding: 5%" + style = "border: 1px dashed black; margin: 5%; padding: 5%" ), # These are the"optional" parameters that need to be filled out if select graphs are selected @@ -51,7 +51,7 @@ ui <- shinyUI({ # Used for C-Rate calculations # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), - style ="border: 1px dashed black; padding: 5%; margin:5%" + style ="border: 1px dashed black; padding: 5%; margin: 5%" ), ), @@ -59,16 +59,16 @@ ui <- shinyUI({ column(4, align ="left", fluidRow( # Presents options for graphs to be generated - "Choose graphs to be generated:", + "Choose graphs to be generated: ", actionButton("whatGraph","What's this?", class ="btn-link"), checkboxGroupInput("gGraphs", NULL, choices = c("Discharge Capacity","Discharge Areal Capacity", "Total Discharge Capacity","Average Voltage","Delta Voltage","Capacity Loss"), inline = FALSE), - "Choose graphs to animate:", + "Choose graphs to animate: ", checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), # "Advanced Analysis", # radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), - # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), - style ="margin: 5%; border: 1px solid black; padding: 5%" + # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), + style ="margin: 5%; border: 1px solid black; padding: 5%" ), ), @@ -79,26 +79,26 @@ ui <- shinyUI({ strong("Files to be Analyzed*"), tags$br(), "Import all Arbin files of interest.", tags$br(), tags$br(), fileInput("files", NULL, multiple = TRUE), - actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width:80%; height:100px; margin:5%; font-size:100%"), - actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width:60%; margin:5%; font-size:100%"), - style = "border: 4px double black; padding: 5%; margin:5%" + actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width: 80%; height: 100px; margin: 5%; font-size: 100%"), + actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width: 60%; margin: 5%; font-size: 100%"), + style = "border: 4px double black; padding: 5%; margin: 5%" ), # This final block enables a button after data becomes available, which trigger the modal to build custom graphs fluidRow( strong("Custom Graph Builder"), tags$br(), "Customize Graphs Once Data is Available", - disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height:50px; margin:5%; font-size:100%")), tags$br(), - style ="border: 1px solid black; padding: 5%; margin:5%" + disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height: 50px; margin: 5%; font-size: 100%")), tags$br(), + style ="border: 1px solid black; padding: 5%; margin: 5%" ), fluidRow( - strong("Optional: Import Active Material Masses from Excel"), tags$br(), + strong("Optional: Import Active Material Masses from Excel"), tags$br(), "Running Analysis without Masses Will Render Raw Capacities (Ah)", textAreaInput("masses", NULL, height = "100px", resize = "vertical"), helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), actionButton("excelImport", "Import"), - style = "border: 1px dashed black; padding: 5%; margin:5%" + style = "border: 1px dashed black; padding: 5%; margin: 5%" ), ), From 6627544fecb3ea8f66fc50d0d7d6a9a9e6cd0515 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Mon, 23 Aug 2021 12:23:37 -0400 Subject: [PATCH 15/19] fixed past data load --- server.R | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/server.R b/server.R index 81b136e..345e89f 100644 --- a/server.R +++ b/server.R @@ -109,13 +109,7 @@ server <- function(input, output, session) { }) observeEvent(input$chooseDir, { - chosenDir <- tk_choose.dir() - x<- 0 - #if (input$batchProcessing) { - # append(dirLocation, chosenDir) - #} else { - dirLocation <<- chosenDir - #} + dirLocation <<- tk_choose.dir() internal_folders <- list.dirs(path = dirLocation, recursive = FALSE) base_names = vector() @@ -126,17 +120,21 @@ server <- function(input, output, session) { if ("history" %in% base_names) { past <- new.env() - load(paste(dirLocation, "/history/", "Formation.RData", sep =""), envir = past) + if (file.exists(paste(dirLocation, "/history/", "Formation.RData", sep =""))) { + load(paste(dirLocation, "/history/", "Formation.RData", sep =""), envir = past) + } else if (file.exists(paste(dirLocation, "/history/", "RateCap.RData", sep =""))) { + load(paste(dirLocation, "/history/", "RateCap.RData", sep =""), envir = past) + } else { + return(NA) + } - if (exists("past$group")) { - if (input$batchProcessing) { - data <<- rbind(data, past$data) - } else { - data <<- past$data - } - - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) + if (input$batchProcessing) { + data <<- rbind(data, past$data) + } else { + data <<- past$dataSubset } + + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) } if (!is.na(dirLocation)) { @@ -622,8 +620,9 @@ server <- function(input, output, session) { } dirName <<- programName + data <- dataSubset - save(dirLocation, dirName, dataSubset, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".Rdata", sep = "")) + save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".Rdata", sep = "")) } # Modal for completed analysis From 36cecaed97047330905c9b463940532f1249c8ca Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Mon, 23 Aug 2021 12:38:42 -0400 Subject: [PATCH 16/19] fixed style --- ui.R | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ui.R b/ui.R index bd82d92..e6a9a0b 100644 --- a/ui.R +++ b/ui.R @@ -20,7 +20,7 @@ ui <- shinyUI({ fluidRow("Analysis Mode", switchInput("batchProcessing", onLabel = "Batch Processing", offLabel = "Single Analysis", handleWidth = "200px") - , align = "center", style = "margin: 0%"), + , align = "center", style = "margin: 0%;"), # This first column is where most user inputs are, with the exception of the directory name column(4, align = "center", @@ -28,16 +28,16 @@ ui <- shinyUI({ # This generates the optional block in which the user can import a previous R environment fluidRow( strong("Start Here"), tags$br(), - "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), + "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width: 80%; margin: 5%; font-size: 100%"), tags$br(), helpText("The analysis will create a folder within the selected folder."), - style = "border: 1px solid black; padding: 5%; margin: 5%" + style = "border: 1px solid black; padding: 5%; margin: 5%;" ), fluidRow( - fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), + fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), actionButton("load", "Load"), - style = "border: 1px dashed black; margin: 5%; padding: 5%" + style = "border: 1px dashed black; margin: 5%; padding: 5%;" ), # These are the"optional" parameters that need to be filled out if select graphs are selected @@ -51,7 +51,7 @@ ui <- shinyUI({ # Used for C-Rate calculations # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), - style ="border: 1px dashed black; padding: 5%; margin: 5%" + style ="border: 1px dashed black; padding: 5%; margin: 5%;" ), ), @@ -68,7 +68,7 @@ ui <- shinyUI({ # "Advanced Analysis", # radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), - style ="margin: 5%; border: 1px solid black; padding: 5%" + style ="margin: 5%; border: 1px solid black; padding: 5%;" ), ), @@ -79,26 +79,26 @@ ui <- shinyUI({ strong("Files to be Analyzed*"), tags$br(), "Import all Arbin files of interest.", tags$br(), tags$br(), fileInput("files", NULL, multiple = TRUE), - actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width: 80%; height: 100px; margin: 5%; font-size: 100%"), - actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width: 60%; margin: 5%; font-size: 100%"), - style = "border: 4px double black; padding: 5%; margin: 5%" + actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width: 80%; height: 100px; margin: 5%; font-size: 100%;"), + actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width: 60%; margin: 5%; font-size: 100%;"), + style = "border: 4px double black; padding: 5%; margin: 5%;" ), # This final block enables a button after data becomes available, which trigger the modal to build custom graphs fluidRow( strong("Custom Graph Builder"), tags$br(), "Customize Graphs Once Data is Available", - disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height: 50px; margin: 5%; font-size: 100%")), tags$br(), - style ="border: 1px solid black; padding: 5%; margin: 5%" + disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height: 50px; margin: 5%; font-size: 100%;")), tags$br(), + style ="border: 1px solid black; padding: 5%; margin: 5%" ), fluidRow( - strong("Optional: Import Active Material Masses from Excel"), tags$br(), + strong("Optional: Import Active Material Masses from Excel"), tags$br(), "Running Analysis without Masses Will Render Raw Capacities (Ah)", textAreaInput("masses", NULL, height = "100px", resize = "vertical"), helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), actionButton("excelImport", "Import"), - style = "border: 1px dashed black; padding: 5%; margin: 5%" + style = "border: 1px dashed black; padding: 5%; margin: 5%;" ), ), From 817e0f37ae30f356f13d04dcc96b30de777105b0 Mon Sep 17 00:00:00 2001 From: rwaldheim Date: Mon, 23 Aug 2021 14:25:21 -0400 Subject: [PATCH 17/19] ran styler --- global.R | 36 +- info.R | 2 +- modal.R | 166 +++-- server.R | 2135 +++++++++++++++++++++++++++++------------------------- ui.R | 212 +++--- 5 files changed, 1349 insertions(+), 1202 deletions(-) diff --git a/global.R b/global.R index aa67795..f762231 100644 --- a/global.R +++ b/global.R @@ -1,38 +1,38 @@ # ###### # # Welcome to the Battery Analyzer Utility! -# +# # This script aims to make it quick and efficient to analyze data exported by an Arbin battery cycler # # ###### - + # ###### -# +# # These are all the required packages to aid in several of the processes, ranging from data analysis to plotting -# +# # ###### # options(warn = 0) list.of.packages <- c("readxl", "dplyr", "shiny", "tcltk", "DT", "shinyjs", "shinyalert", "pracma", "purrr", "zoo", "plotrix", "tools", "shinyWidgets", "gifski") -new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] -if(length(new.packages)) install.packages(new.packages) +new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[, "Package"])] +if (length(new.packages)) install.packages(new.packages) lapply(list.of.packages, require, character.only = TRUE) -source('ui.R', local = TRUE) -source('server.R') -source('info.R') +source("ui.R", local = TRUE) +source("server.R") +source("info.R") # This line tests if the current R environment is interactive, RStudio makes an interactive environment by default if (interactive()) { - + # ###### - # + # # All the global variables within the script, aka variables that need to be accessed by more than one # function or session # - #"Reactive Values" are ones that need to be readily changed, such as user inputs and variables to be displayed - # + # "Reactive Values" are ones that need to be readily changed, such as user inputs and variables to be displayed + # # ###### data <- reactiveValues(data = data.frame()) final <- data.frame() @@ -44,14 +44,14 @@ if (interactive()) { tmp_data <- data.frame() dirName <<- "" tmp_cycles <- vector() - titleLabel <-"" - xlabel <-"" - ylabel <-"" + titleLabel <- "" + xlabel <- "" + ylabel <- "" addParams <- FALSE arbinCM <- FALSE catMetric <<- vector() - legTitle <<-"" - sheetName <<-"" + legTitle <<- "" + sheetName <<- "" bounds <<- vector() compCycleFacts <<- data.frame() } diff --git a/info.R b/info.R index 1f477df..a0f2664 100644 --- a/info.R +++ b/info.R @@ -83,4 +83,4 @@ graphInfoTable <- HTML('
Graph X Axis Y AxisPlot Frequency
Plot Frequency
Description
Cycle Discharge Capacity (mAh/g or Ah) per cellDischarge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.Discharge capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis. Units depend if the masses are specified.
Discharge Areal Capacity Cycle Discharge Capacity (Ah/cm2) per cellDischarge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.Discharge areal capacity for each individual cell per cycle. Coulombic efficiency is also plotted on a secondary axis.
Total Discharge Capacity Cycle Discharge Capacity (mAh/g or Ah) per analysisDischarge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.Discharge capacity summarized for all cells in the analysis. Coulombic efficiency is also plotted on a secondary axis. Mean is plotted as a point with error bars presenting the standard error between the cells. Units depend if the masses are specified.
Average VoltageThe discharge capacity minus the charge capacity for each cycle. Units depend if the masses are specified.
- ') \ No newline at end of file + ') diff --git a/modal.R b/modal.R index 2cabad1..07cc9a3 100644 --- a/modal.R +++ b/modal.R @@ -1,89 +1,97 @@ # Defines the modal in which the cell masses can be exported from Excel -graphModal <- modalDialog({ - fluidPage(style ="font-size:15pt;", - tags$head(tags$style(".modal-dialog{min-width:60%}")), - - fluidRow(align ="center", graphInfoTable), - - )}, title ="Graph Types", easyClose = TRUE) +graphModal <- modalDialog( + { + fluidPage( + style = "font-size:15pt;", + tags$head(tags$style(".modal-dialog{min-width:60%}")), + fluidRow(align = "center", graphInfoTable), + ) + }, + title = "Graph Types", + easyClose = TRUE +) -graphbuilder <- modalDialog({ - fluidPage( - useShinyjs(), - useShinyalert(), - - tags$head(tags$style(".modal-dialog{width:80%}")), - tags$head(tags$style(".modal-body{ min-height:1000px}")), - - sidebarLayout( - - sidebarPanel( - fluidRow( - headerPanel("Graph Options"), - ), - - fluidRow(style = "padding:5%; border: 1px solid black;", - strong("Basis for Plot Types"), tags$br(), - helpText("Between Analysis requires a second dataset to be imported"), tags$br(), - radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), - hidden(fileInput("compAnalysis", "Data to Compare")) - ), - - fluidRow(style ="padding:5%; margin:5%;", - radioButtons("typeGraph","Graph Type:", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", - "Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" ), inline = FALSE), - radioButtons("plotStyle","Plot Style:", choiceNames = c("Point","Line","Both"), choiceValues = c("p","l","o"), inline = TRUE), - checkboxGroupInput("cells","Cell to Analyze:", choices = 1, inline = FALSE), - selectInput("renderCycles","Cycles of Interest:", choices = 1, multiple = TRUE), - ), - - fluidRow( - strong("Click to show coordinates:"), tags$br(), tags$br(), - "X: ", - textOutput("hoverCoordx", inline = TRUE), tags$br(), - "Y: ", - textOutput("hoverCoordy", inline = TRUE), - style ="border: 1px solid black; padding: 5%; margin: 5%;" - ), - - fluidRow( - textInput("fileName","Name of graph file:"), - actionButton("saveGraph","Save Graph", width = '100%', class = 'btn-primary'), - style ="border: 4px double black; padding: 5%; margin: 5%;" - ), - ), - - mainPanel( - fluidRow( - plotOutput("outputPlot", height ="800px", click = "plot_click"), - style = "padding: 5%;", - ), - fluidRow( - h3("Graph Formatting"), - helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), - column(2, - numericInput("xMin", "X Min", value = NULL), - sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), +graphbuilder <- modalDialog( + { + fluidPage( + useShinyjs(), + useShinyalert(), + tags$head(tags$style(".modal-dialog{width:80%}")), + tags$head(tags$style(".modal-body{ min-height:1000px}")), + sidebarLayout( + sidebarPanel( + fluidRow( + headerPanel("Graph Options"), ), - column(2, - numericInput("xMax", "X Max", value = NULL), - sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + fluidRow( + style = "padding:5%; border: 1px solid black;", + strong("Basis for Plot Types"), tags$br(), + helpText("Between Analysis requires a second dataset to be imported"), tags$br(), + radioButtons("perType", NULL, choices = c("Within Analysis", "Between Analyses"), inline = TRUE), + hidden(fileInput("compAnalysis", "Data to Compare")) ), - column(2, - numericInput("yMin", "Y Min", value = NULL), + fluidRow( + style = "padding:5%; margin:5%;", + radioButtons("typeGraph", "Graph Type:", choices = c( + "dQdV Graphs", "Voltage Profiles", "Voltage vs. Time", + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" + ), inline = FALSE), + radioButtons("plotStyle", "Plot Style:", choiceNames = c("Point", "Line", "Both"), choiceValues = c("p", "l", "o"), inline = TRUE), + checkboxGroupInput("cells", "Cell to Analyze:", choices = 1, inline = FALSE), + selectInput("renderCycles", "Cycles of Interest:", choices = 1, multiple = TRUE), ), - column(2, - numericInput("yMax", "Y Max", value = NULL), + fluidRow( + strong("Click to show coordinates:"), tags$br(), tags$br(), + "X: ", + textOutput("hoverCoordx", inline = TRUE), tags$br(), + "Y: ", + textOutput("hoverCoordy", inline = TRUE), + style = "border: 1px solid black; padding: 5%; margin: 5%;" ), - column(4, - textInput("originalData", "Original Data Name", value = "Original Data"), - textInput("compareData", "Comparison Data Name", value = "Comparison Data"), + fluidRow( + textInput("fileName", "Name of graph file:"), + actionButton("saveGraph", "Save Graph", width = "100%", class = "btn-primary"), + style = "border: 4px double black; padding: 5%; margin: 5%;" ), - style ="border: 1px dashed black; padding: 2%;", + ), + mainPanel( + fluidRow( + plotOutput("outputPlot", height = "800px", click = "plot_click"), + style = "padding: 5%;", + ), + fluidRow( + h3("Graph Formatting"), + helpText("*If left blank, they will be calculated using the min and max of the data to be plotted."), + column( + 2, + numericInput("xMin", "X Min", value = NULL), + sliderInput("textSize", "Text Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column( + 2, + numericInput("xMax", "X Max", value = NULL), + sliderInput("pointSize", "Point/Line Size", min = 0.1, max = 5, value = 1, ticks = FALSE), + ), + column( + 2, + numericInput("yMin", "Y Min", value = NULL), + ), + column( + 2, + numericInput("yMax", "Y Max", value = NULL), + ), + column( + 4, + textInput("originalData", "Original Data Name", value = "Original Data"), + textInput("compareData", "Comparison Data Name", value = "Comparison Data"), + ), + style = "border: 1px dashed black; padding: 2%;", + ) ) ) ) - ) -}, size ="l", title ="Post-Processing Graph Builder") - + }, + size = "l", + title = "Post-Processing Graph Builder" +) diff --git a/server.R b/server.R index 345e89f..daebb77 100644 --- a/server.R +++ b/server.R @@ -1,1097 +1,1236 @@ library(shiny) server <- function(input, output, session) { + utils <- c("modal.R") + lapply(utils, source) - utils <- c("modal.R") - lapply(utils, source) - - # This sets the maximum file size Shiny will import, the default of 5Mb is not large enough to handle Arbin files - options(shiny.maxRequestSize=1000*1024^2) - - split_path <- function(x) if (dirname(x)==x) x else c(basename(x),split_path(dirname(x))) - - se <- function(x) {sd(x) / length(x)} - - proxy = dataTableProxy("channels") - - batch.elements <- c("rerun", "load", "area", "whatGraph", "gGraphs", "gAnim", "graphBuilder") - - deleteButtonCol <- function(df, id, ...) { - f <- function(i) { - as.character( - actionButton( - paste(id, i, sep = "_"), - label = icon("trash", lib = "glyphicon"), - onclick = 'Shiny.setInputValue(\"deletePressed\", this.id, {priority: "event"})' - ) + # This sets the maximum file size Shiny will import, the default of 5Mb is not large enough to handle Arbin files + options(shiny.maxRequestSize = 1000 * 1024^2) + + split_path <- function(x) if (dirname(x) == x) x else c(basename(x), split_path(dirname(x))) + + se <- function(x) { + sd(x) / length(x) + } + + proxy <- dataTableProxy("channels") + + batch.elements <- c("rerun", "load", "area", "whatGraph", "gGraphs", "gAnim", "graphBuilder") + + deleteButtonCol <- function(df, id, ...) { + f <- function(i) { + as.character( + actionButton( + paste(id, i, sep = "_"), + label = icon("trash", lib = "glyphicon"), + onclick = 'Shiny.setInputValue(\"deletePressed\", this.id, {priority: "event"})' ) - } - - deleteCol <- unlist(lapply(seq_len(nrow(df)), f)) - - cbind(data, delete = deleteCol) + ) } - - generateDataTable <- function(df) { - DT::datatable(df,escape = FALSE, editable = FALSE, options = list(columnDefs = list(list(visible=FALSE, targets=c(5, 6, 7)))), - colnames = c("Group","Program","File","Sheet","Mass (g)","Filepath","Group Path","Limiting Electrode Area (cm^2)", "Delete"), rownames = FALSE) + + deleteCol <- unlist(lapply(seq_len(nrow(df)), f)) + + cbind(data, delete = deleteCol) + } + + generateDataTable <- function(df) { + DT::datatable(df, + escape = FALSE, editable = FALSE, options = list(columnDefs = list(list(visible = FALSE, targets = c(5, 6, 7)))), + colnames = c("Group", "Program", "File", "Sheet", "Mass (g)", "Filepath", "Group Path", "Limiting Electrode Area (cm^2)", "Delete"), rownames = FALSE + ) + } + + parseDeleteEvent <- function(idstr) { + res <- as.integer(sub(".*_([0-9]+)", "\\1", idstr)) + if (!is.na(res)) { + res } - - parseDeleteEvent <- function(idstr) { - res <- as.integer(sub(".*_([0-9]+)", "\\1", idstr)) - if (! is.na(res)) { - res + } + + observeEvent(input$deletePressed, { + rowNum <- parseDeleteEvent(input$deletePressed) + + data <<- data[-rowNum, ] + + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) + }) + + observeEvent(input$batchProcessing, { + if (input$batchProcessing) { + lapply(batch.elements, hide) + } else { + lapply(batch.elements, show) + } + }) + + observeEvent(input$clearTable, { + shinyalert("Warning!", "Are you sure you wish to clear all cells?", + type = "warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", + callbackR = function(x) { + if (x) { + data <<- data[0, ] + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) + } } + ) + }) + + export_to_origin <- function() { + if (!("reticulate" %in% installed.packages()[, "Package"])) { + install.packages("reticulate") } - - observeEvent(input$deletePressed, { - rowNum <- parseDeleteEvent(input$deletePressed) - - data <<- data[-rowNum,] - - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) - }) - - observeEvent(input$batchProcessing, { - if (input$batchProcessing) { - lapply(batch.elements, hide) + require(reticulate) + + py_location <- py_config() + + py_install("OriginExt", pip = TRUE) + py_install("pandas", pip = TRUE) + + filtered_location <- shQuote(paste(dirLocation, "/", input$dirName, sep = "")) + + system(paste(py_location$python, " rPyO.py ", filtered_location, sep = "")) + } + + # Ensures the files imported for analysis are Excel files + observeEvent(input$files, { + validFile <- FALSE + + for (file in 1:nrow(input$files)) { + if (file_ext(input$files[file, "name"]) == "xlsx" | file_ext(input$files[file, "name"]) == "xls") { + validFile <- TRUE + arbinCM <<- FALSE + } else if (file_ext(file) == "csv") { + arbinCM <<- TRUE + validFile <- TRUE + } + } + + if (validFile) { + renderTable() + } else { + shinyalert("That isn't right...", "Please upload an Excel or CSV file.", "error") + } + }) + + observeEvent(input$chooseDir, { + dirLocation <<- tk_choose.dir() + + internal_folders <- list.dirs(path = dirLocation, recursive = FALSE) + base_names <- vector() + for (folder in internal_folders) { + base_names <- append(base_names, split_path(folder)[1]) + } + + if ("history" %in% base_names) { + past <- new.env() + + if (file.exists(paste(dirLocation, "/history/", "Formation.RData", sep = ""))) { + load(paste(dirLocation, "/history/", "Formation.RData", sep = ""), envir = past) + } else if (file.exists(paste(dirLocation, "/history/", "RateCap.RData", sep = ""))) { + load(paste(dirLocation, "/history/", "RateCap.RData", sep = ""), envir = past) } else { - lapply(batch.elements, show) + return(NA) } - }) - - observeEvent(input$clearTable, { - shinyalert("Warning!", "Are you sure you wish to clear all cells?", - type ="warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", - callbackR = function(x) { - if (x) { - data <<- data[0,] - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) - } - }) - }) - - export_to_origin <- function() { - if (!("reticulate" %in% installed.packages()[, "Package"])) { - install.packages("reticulate") + + if (input$batchProcessing) { + data <<- rbind(data, past$data) + } else { + data <<- past$dataSubset } - require(reticulate) - - py_location <- py_config() - - py_install("OriginExt", pip = TRUE) - py_install("pandas", pip = TRUE) - - filtered_location <- shQuote(paste(dirLocation, "/", input$dirName, sep = '')) - - system(paste(py_location$python, " rPyO.py ", filtered_location, sep='')) + + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) } - - # Ensures the files imported for analysis are Excel files - observeEvent(input$files, { - validFile <- FALSE - - for (file in 1:nrow(input$files)) { - if (file_ext(input$files[file, "name"]) == "xlsx" | file_ext(input$files[file, "name"]) == "xls") { - validFile <- TRUE - arbinCM <<- FALSE - } else if (file_ext(file) == "csv") { - arbinCM <<- TRUE - validFile <- TRUE - } + + if (!is.na(dirLocation)) { + output$currDir <- renderText({ + paste(split_path(dirLocation)[1], "(", split_path(dirLocation)[2], ")") + }) + } + }) + + # Method for importing the previous R environment + observeEvent(input$load, { + if (is.null(input$rerun)) { + shinyalert("Uh oh!", "It appears you haven't selected a .RData file to import.", "error") + } else { + load(input$rerun$datapath[[1]]) + + + if (file_ext(input$rerun$datapath) == "RData") { + validFile <- TRUE } - + if (validFile) { - renderTable() + data <<- dataSubset + dirLocation <<- dirLocation + numCycles <<- numCycles + dQdVData <<- dQdVData + total <<- total + cycle_facts <<- cycle_facts + + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) + + enable("graphBuilder") } else { - shinyalert("That isn't right...","Please upload an Excel or CSV file.","error") - } - }) - - observeEvent(input$chooseDir, { - dirLocation <<- tk_choose.dir() - - internal_folders <- list.dirs(path = dirLocation, recursive = FALSE) - base_names = vector() - for (folder in internal_folders) { - base_names <- append(base_names, split_path(folder)[1]) - } - - if ("history" %in% base_names) { - past <- new.env() - - if (file.exists(paste(dirLocation, "/history/", "Formation.RData", sep =""))) { - load(paste(dirLocation, "/history/", "Formation.RData", sep =""), envir = past) - } else if (file.exists(paste(dirLocation, "/history/", "RateCap.RData", sep =""))) { - load(paste(dirLocation, "/history/", "RateCap.RData", sep =""), envir = past) - } else { - return(NA) - } - - if (input$batchProcessing) { - data <<- rbind(data, past$data) - } else { - data <<- past$dataSubset - } - - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) - } - - if (!is.na(dirLocation)) { - output$currDir <- renderText({paste(split_path(dirLocation)[1], "(", split_path(dirLocation)[2], ")")}) + shinyalert("That isn't right...", "Please upload an RData file.", "error") } - }) - - # Method for importing the previous R environment - observeEvent(input$load, { - if (is.null(input$rerun)) { - shinyalert("Uh oh!", "It appears you haven't selected a .RData file to import.", "error") - } else { - load(input$rerun$datapath[[1]]) - + } + }) - if (file_ext(input$rerun$datapath) == "RData") { - validFile <- TRUE - } - - if (validFile) { - data <<- dataSubset - dirLocation <<- dirLocation - numCycles <<- numCycles - dQdVData <<- dQdVData - total <<- total - cycle_facts <<- cycle_facts - - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) - - enable("graphBuilder") - } else { - shinyalert("That isn't right...","Please upload an RData file.","error") - } - } - }) - - observeEvent(input$whatGraph, { - showModal(graphModal) - }) - - # Data validation the masses imported from Excel, if valid they are placed into the datatable - observeEvent(input$excelImport, { - if (length(names(data)) <= 1) { - shinyalert("Uh oh!","You need to import cells first!","error") - removeModal() - } else { - tryCatch({ + observeEvent(input$whatGraph, { + showModal(graphModal) + }) + + # Data validation the masses imported from Excel, if valid they are placed into the datatable + observeEvent(input$excelImport, { + if (length(names(data)) <= 1) { + shinyalert("Uh oh!", "You need to import cells first!", "error") + removeModal() + } else { + tryCatch( + { masses <- lapply(strsplit(strRep(input$masses, "\n", ","), ",", fixed = TRUE), as.double) data$Mass <<- masses[[1]] - }, error = function(cond) { + }, + error = function(cond) { print(cond) - shinyalert("Something isn't right...","The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.","error") + shinyalert("Something isn't right...", "The number of masses imported did not match the amount of cells present or the text contained some special characters. Please try again.", "error") removeModal() - }, finally = { - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) - }) - } - }) - - # After the validation of the Arbin files, they macros (file name, and sheets) are taken and rendered in to a datatable - renderTable <- function() { - - files <- input$files - - if (is.null(files)) { - return(NULL) - } - - file_sheet <- data.frame() - if (arbinCM) { - raw_data <- data.frame(name = files[["name"]], "sheet" = 1:nrow(files), datapath = files[["datapath"]]) - } else { - for (i in 1:nrow(files)) { - sheets <- excel_sheets(files[i, 4]) - file_sheet <- rbind(file_sheet, data.frame(group = basename(dirLocation), program = rep(input$dirName, length(sheets)), name = rep(files[["name"]][i], length(sheets)),"sheet" = sheets,"Mass" = rep(0, length(sheets)), - datapath = rep(files[["datapath"]][i], length(sheets)), grouppath = rep(dirLocation, length(sheets)), area = rep(input$area, length(sheets)))) + }, + finally = { + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) } - - raw_data <- filter(file_sheet, grepl('Channel', sheet) & !grepl('Chart', sheet)) + ) + } + }) + + # After the validation of the Arbin files, they macros (file name, and sheets) are taken and rendered in to a datatable + renderTable <- function() { + files <- input$files + + if (is.null(files)) { + return(NULL) + } + + file_sheet <- data.frame() + if (arbinCM) { + raw_data <- data.frame(name = files[["name"]], "sheet" = 1:nrow(files), datapath = files[["datapath"]]) + } else { + for (i in 1:nrow(files)) { + sheets <- excel_sheets(files[i, 4]) + file_sheet <- rbind(file_sheet, data.frame( + group = basename(dirLocation), program = rep(input$dirName, length(sheets)), name = rep(files[["name"]][i], length(sheets)), "sheet" = sheets, "Mass" = rep(0, length(sheets)), + datapath = rep(files[["datapath"]][i], length(sheets)), grouppath = rep(dirLocation, length(sheets)), area = rep(input$area, length(sheets)) + )) } - - if (!is.null(dim(data)[1]) & !is.null(size(raw_data))) { - new_rows <- intersect(raw_data$sheet, data[data["group"] == basename(dirLocation)]$sheet) - data <<- data[which(data$sheet %in% new_rows),] - data$datapath <<- raw_data$datapath - } else { - if (input$batchProcessing) { - tryCatch( { + + raw_data <- filter(file_sheet, grepl("Channel", sheet) & !grepl("Chart", sheet)) + } + + if (!is.null(dim(data)[1]) & !is.null(size(raw_data))) { + new_rows <- intersect(raw_data$sheet, data[data["group"] == basename(dirLocation)]$sheet) + data <<- data[which(data$sheet %in% new_rows), ] + data$datapath <<- raw_data$datapath + } else { + if (input$batchProcessing) { + tryCatch( + { data <<- rbind(data, raw_data) - }, error = function(cond) { + }, + error = function(cond) { data <<- raw_data - }) - } else { - data <<- raw_data - } - } - - if (arbinCM) { - output$channels <- renderDataTable(data, editable = TRUE, options = list(columnDefs = list(list(visible=FALSE, targets=c(3)))), - colnames = c("File", "Sheet", "Filepath")) + } + ) } else { - output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, 'delete_button'))) + data <<- raw_data } } - - # After some data validation, the main analysis is run on click of the"Run Analysis" button - observeEvent(input$submit, { - if (length(names(data)) <= 1) { - shinyalert("Uh oh!", "You need to import cells first!", "error") - } else if (is.na(dirLocation) | dirLocation == "") { - shinyalert("Uh oh!", "You need to enter a directory name first!", "error") - } else if (input$dirName == "") { - shinyalert("Uh oh!", "You need to enter an analysis name first!", "error") - } else if (sum(data$Mass) == 0) { - shinyalert("Uh oh!", "You have not entered any masses. Do you wish to continue?", - type ="warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", - callbackR = function(x) { - if (x) { - runscript() - } - } - ) - } else { - runscript() + + if (arbinCM) { + output$channels <- renderDataTable(data, + editable = TRUE, options = list(columnDefs = list(list(visible = FALSE, targets = c(3)))), + colnames = c("File", "Sheet", "Filepath") + ) + } else { + output$channels <- renderDataTable(generateDataTable(deleteButtonCol(data, "delete_button"))) + } + } + + # After some data validation, the main analysis is run on click of the"Run Analysis" button + observeEvent(input$submit, { + if (length(names(data)) <= 1) { + shinyalert("Uh oh!", "You need to import cells first!", "error") + } else if (is.na(dirLocation) | dirLocation == "") { + shinyalert("Uh oh!", "You need to enter a directory name first!", "error") + } else if (input$dirName == "") { + shinyalert("Uh oh!", "You need to enter an analysis name first!", "error") + } else if (sum(data$Mass) == 0) { + shinyalert("Uh oh!", "You have not entered any masses. Do you wish to continue?", + type = "warning", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Continue", cancelButtonText = "Abort", + callbackR = function(x) { + if (x) { + runscript() + } + } + ) + } else { + runscript() + } + }) + + # This function responsible for analysis of the + runscript <- function() { + groupList <- unique(data[["group"]]) + + for (group in groupList) { + dataSubset <- data[data[["group"]] == group, ] + dirLocation <- dataSubset[["grouppath"]][1] + programName <- dataSubset[["program"]][1] + + # Sets up a progress bar in which to estimate how long the execution of the code will take + progress <- Progress$new(session, min = 0, max = nrow(dataSubset)) + progress$set(message = paste(group, ": Plugging and chugging...\n"), detail = "Starting up...") + + # Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) + while (dev.cur() != 1) { + dev.off() } - }) - - # This function responsible for analysis of the - runscript <- function() { - - groupList <- unique(data[["group"]]) - - for (group in groupList) { - - dataSubset <- data[data[["group"]] == group,] - dirLocation <- dataSubset[["grouppath"]][1] - programName <- dataSubset[["program"]][1] - - # Sets up a progress bar in which to estimate how long the execution of the code will take - progress <- Progress$new(session, min = 0, max = nrow(dataSubset)) - progress$set(message = paste(group, ": Plugging and chugging...\n"), detail ="Starting up...") - - # Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) - while (dev.cur() != 1) { - dev.off() + + # Resets the variables for the graph builder so new results are concatenated to old ones + numCycles <<- data.frame() + dQdVData <<- data.frame() + total <<- data.frame() + cycle_facts <<- data.frame() + + # Disable all input fields to prevent errors occurring from changing values + disable("files") + disable("lowV") + disable("highV") + disable("dirLocation") + disable("submit") + disable("excelImport") + disable("gGraphs") + disable("peakFit") + disable("area") + disable("perActive") + disable("capActive") + + # Creates the directory in which all dataSubset will be stored + dir.create(paste(dirLocation, programName, sep = "/")) + + # Update the status once all set-up functions are complete + progress$set(detail = "Starting first cell...") + + # ###### + # + # The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. + # + # ###### + for (row in 1:nrow(dataSubset)) { + + # ###### + # + # This is where all code that should be executed on a"per cell" basis, to prepare for analysis + # + # ###### + + # Import the excel sheet corresponding to cell of interest + if (arbinCM) { + tmp_excel <- read.csv(toString(dataSubset$dataSubsetpath[row])) + names(tmp_excel) <- c("Index", "Test_Time(s)", "Date_Time", "Step_Time(s)", "Step_Index", "Cycle_Index", "Current(A)", "Voltage(V)", "Charge_Capacity(Ah)", "Discharge_Capacity(Ah)", "Charge_Energy(Wh)", "Discharge_Energy(Wh)") + } else { + tmp_excel <- read_excel(toString(dataSubset$datapath[row]), toString(dataSubset$sheet[row])) } - - # Resets the variables for the graph builder so new results are concatenated to old ones - numCycles <<- data.frame() - dQdVData <<- data.frame() - total <<- data.frame() - cycle_facts <<- data.frame() - - # Disable all input fields to prevent errors occurring from changing values - disable("files") - disable("lowV") - disable("highV") - disable("dirLocation") - disable("submit") - disable("excelImport") - disable("gGraphs") - disable("peakFit") - disable("area") - disable("perActive") - disable("capActive") - - # Creates the directory in which all dataSubset will be stored - dir.create(paste(dirLocation, programName, sep = "/")) - - # Update the status once all set-up functions are complete - progress$set(detail ="Starting first cell...") - + + # Create an nested directory for all the dataSubset and, if applicable, then further folders for graphs of interest + dir.create(paste(dirLocation, "/", programName, dataSubset$sheet[row], sep = "/")) + if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row], "dQdV Plots", sep = "/")) + if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row], "Voltage Profiles", sep = "/")) + if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row], "Voltage v Time", sep = "/")) + + # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis + if (sum(dataSubset$Mass) != 0) { + ylabel <- "Capacity (mAh/g)" + + tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / dataSubset$Mass[row])) + tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)` * (1000 / dataSubset$Mass[row])) + + tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c + tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 + } else { + ylabel <- "Capacity (Ah)" + + tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` + tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 + } + tmp_excel$Cell <- row + tmp_excel$CE[is.infinite(tmp_excel$CE) | is.nan(tmp_excel$CE) | tmp_excel$CE > 200] <- 0 + # ###### - # - # The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. - # + # + # This loop iterates through each cycle of the cell. + # # ###### - for (row in 1:nrow(dataSubset)) { - - # ###### - # - # This is where all code that should be executed on a"per cell" basis, to prepare for analysis - # - # ###### - - # Import the excel sheet corresponding to cell of interest - if (arbinCM) { - tmp_excel <- read.csv(toString(dataSubset$dataSubsetpath[row])) - names(tmp_excel) <- c("Index", "Test_Time(s)", "Date_Time", "Step_Time(s)", "Step_Index", "Cycle_Index", "Current(A)", "Voltage(V)", "Charge_Capacity(Ah)", "Discharge_Capacity(Ah)", "Charge_Energy(Wh)", "Discharge_Energy(Wh)") - } else { - tmp_excel <- read_excel(toString(dataSubset$datapath[row]), toString(dataSubset$sheet[row])) - } - - # Create an nested directory for all the dataSubset and, if applicable, then further folders for graphs of interest - dir.create(paste(dirLocation, "/", programName, dataSubset$sheet[row], sep ="/")) - if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"dQdV Plots", sep ="/")) - if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"Voltage Profiles", sep ="/")) - if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation, programName, dataSubset$sheet[row],"Voltage v Time", sep ="/")) - - # Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis - if (sum(dataSubset$Mass) != 0) { - ylabel <-"Capacity (mAh/g)" - - tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / dataSubset$Mass[row])) - tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)`* (1000 / dataSubset$Mass[row])) - - tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c - tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 - } else { - ylabel <-"Capacity (Ah)" - - tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` - tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 - } - tmp_excel$Cell <- row - tmp_excel$CE[is.infinite(tmp_excel$CE)|is.nan(tmp_excel$CE)|tmp_excel$CE > 200] <- 0; - + cycles <- split(tmp_excel, tmp_excel$Cycle_Index) + prev_c <- 0 + lastCC <- 0 + ch_dch <- TRUE + durations <- vector(length = 4) + caps <- vector(length = 4) + prev <- TRUE + dchV <- 0 + chV <- 0 + i <- 1 + for (cycle in cycles) { + # ###### - # - # This loop iterates through each cycle of the cell. - # + # + # Within each cycle, take out the individual steps. These include the charge, discharge, and others. + # # ###### - cycles <- split(tmp_excel, tmp_excel$Cycle_Index) - prev_c <- 0 - lastCC <- 0 - ch_dch <- TRUE - durations <- vector(length = 4) - caps <- vector(length = 4) - prev <- TRUE - dchV <- 0 - chV <- 0 - i <- 1 - for (cycle in cycles) { - + + progress$set(detail = paste("Analyzing cell", row, ", cycle", i)) + + steps <- split(cycle, cycle$Step_Index) + n <- 1 + + for (step in steps) { + # ###### - # - # Within each cycle, take out the individual steps. These include the charge, discharge, and others. - # + # + # Isolation of the charge and discharge cycles. The algorithm is as follows: + # + # If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines + # if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). + # # ###### - - progress$set(detail = paste("Analyzing cell", row,", cycle", i)) - - steps <- split(cycle, cycle$Step_Index) - n <- 1 - - for (step in steps) { - + if (abs(tail(step$"Voltage(V)", 1) - step$"Voltage(V)"[[1]]) > 0.5) { + # ###### - # - # Isolation of the charge and discharge cycles. The algorithm is as follows: - # - # If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines - # if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). - # + # + # All code that should be executed for every charge/discharge cycles should be written here. + # # ###### - if (abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) > 0.5) { - - # ###### - # - # All code that should be executed for every charge/discharge cycles should be written here. - # - # ###### - lastCC <- n - if (step$'Current(A)'[[1]] > 0) { - chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) - dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1), CC=step[['CC']][1:nrow(step)])) - - durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] - ch_dch <- TRUE - } else { - dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`,1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) - dQDdV <- diff(step$`Discharge_Capacity(Ah)`)/diff(step$`Voltage(V)`) - - durations[3] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] - ch_dch <- FALSE - if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1), CC=step[['CC']][1:nrow(step)])) - prev_c = step$`Current(A)`[[1]] - } else { - if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1), CC=step[['CC']][1:nrow(step)])) - } - } - } else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { - if (step$'Current(A)'[[1]] > 0) { - durations[2] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[2] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] + lastCC <- n + if (step$"Current(A)"[[1]] > 0) { + chV <- (1 / (tail(step$`Charge_Capacity(Ah)`, 1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) + dQCdV <- diff(step$`Charge_Capacity(Ah)`) / diff(step$`Voltage(V)`) + dQdVData <<- rbind(dQdVData, data.frame(cycle = rep(i, length(dQCdV) + 1), cell = rep(row, length(dQCdV) + 1), c_d = rep(0, length(dQCdV) + 1), voltage = step$`Voltage(V)`, dQdV = c(0, dQCdV), F_L = rep(0, length(dQCdV) + 1), CC = step[["CC"]][1:nrow(step)])) + + durations[1] <- tail(step$"Test_Time(s)", 1) - step$"Test_Time(s)"[[1]] + caps[1] <- tail(step$"Charge_Capacity(Ah)", 1) - step$"Charge_Capacity(Ah)"[[1]] + ch_dch <- TRUE + } else { + dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`, 1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) + dQDdV <- diff(step$`Discharge_Capacity(Ah)`) / diff(step$`Voltage(V)`) + + durations[3] <- tail(step$"Test_Time(s)", 1) - step$"Test_Time(s)"[[1]] + caps[3] <- tail(step$"Discharge_Capacity(Ah)", 1) - step$"Discharge_Capacity(Ah)"[[1]] + ch_dch <- FALSE + if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle = rep(i, length(dQDdV) + 1), cell = rep(row, length(dQDdV) + 1), c_d = rep(1, length(dQDdV) + 1), voltage = step$`Voltage(V)`, dQdV = c(0, dQDdV), F_L = rep(1, length(dQDdV) + 1), CC = step[["CC"]][1:nrow(step)])) + prev_c <- step$`Current(A)`[[1]] } else { - durations[4] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] - caps[4] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] + if (!arbinCM) dQdVData <<- rbind(dQdVData, data.frame(cycle = rep(i, length(dQDdV) + 1), cell = rep(row, length(dQDdV) + 1), c_d = rep(1, length(dQDdV) + 1), voltage = step$`Voltage(V)`, dQdV = c(0, dQDdV), F_L = rep(0, length(dQDdV) + 1), CC = step[["CC"]][1:nrow(step)])) } } - ch_dch <- FALSE - n <- n + 1 - } - - # ###### - # - # Code meant to be run on dataSubset"per cycle" should be written here - # - # ###### - - dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] - dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] - - if (sum(dataSubset$Mass) != 0) { - DCap <- tail(cycle$Q.d, 1) - CCap <- tail(cycle$Q.c, 1) - } else { - DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) - CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) - } - - timeCVFracCh <- durations[1] / (durations[1] + durations[2]) - timeCVFracDch <- durations[3] / (durations[3] + durations[4]) - capCVFracCh <- caps[1] / (caps[1] + caps[2]) - capCVFracDch <- caps[3] / (caps[3] + caps[4]) - - if (max(tmp_excel$voltage) > 2) { - CE <- DCap / CCap - } else { - CE <- CCap / DCap + } else if (n - lastCC == 1 & abs(tail(step$"Voltage(V)", 1) - step$"Voltage(V)"[[1]]) < 0.001) { + if (step$"Current(A)"[[1]] > 0) { + durations[2] <- tail(step$"Test_Time(s)", 1) - step$"Test_Time(s)"[[1]] + caps[2] <- tail(step$"Charge_Capacity(Ah)", 1) - step$"Charge_Capacity(Ah)"[[1]] + } else { + durations[4] <- tail(step$"Test_Time(s)", 1) - step$"Test_Time(s)"[[1]] + caps[4] <- tail(step$"Discharge_Capacity(Ah)", 1) - step$"Discharge_Capacity(Ah)"[[1]] + } } - - # Record charge and discharge voltage, then calculate the delta and average voltage - cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, - dV=chV-dchV, DCap = DCap, raw_DCap = tail(cycle$'Discharge_Capacity(Ah)', 1), raw_CCap = tail(cycle$'Charge_Capacity(Ah)', 1), CCap = CCap, CE = CE * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, - cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, - capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) - - i <- i + 1 + ch_dch <- FALSE + n <- n + 1 } - + # ###### - # - # Code meant to be run on dataSubset"per cell" should be written here - # + # + # Code meant to be run on dataSubset"per cycle" should be written here + # # ###### - - cell_dataSubset <- cycle_facts[cycle_facts$cell == row,] - - # Discharge capacity plotting, with coulombic efficiency being plotted alongside - if (is.element("Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Discharge Capacity Plot.png", sep ="")) - eol <- cell_dataSubset$`DCap`[[1]] * 0.8 - plot(cell_dataSubset$cycle, cell_dataSubset$DCap, type ="p", main=paste("Discharge Capacity for", programName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) - abline(h=eol, lty ="dotted") - par(new = T) - plot(cell_dataSubset$cycle, cell_dataSubset$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") - axis(side = 4, col ="red", col.axis = "red") - dev.off() - } - - # Discharge areal capacity plotting, with coulombic efficiency being plotted alongside - if (is.element("Discharge Areal Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) - new_par <- old_par <- par("mar") - new_par[4] <- old_par[2] - par(mar = new_par) - eol <- ((cell_dataSubset$DCap[[1]] * 1000) / dataSubset$area[row]) * 0.8 - plot(cell_dataSubset$cycle, ((cell_dataSubset$DCap * 1000) / dataSubset$area[row]), type ="p", main=paste("Discharge Areal Capacity for", programName), xlab=NA, ylab="Discharge Capacity (mAh/cm^2)", mai = c(1,1,1,2)) - abline(h=eol, lty ="dotted") - par(new = T) - plot(cell_dataSubset$cycle, cell_dataSubset$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") - axis(side = 4, col ="red", col.axis = "red") - dev.off() - } - - - # Average voltage plotting - if (is.element("Average Voltage", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Average Voltage Plot.png", sep ="")) - plot(cell_dataSubset$cycle, cell_dataSubset$chV, col="blue", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_dataSubset[,2:4]), max(cell_dataSubset[,2:4]))) - points(cell_dataSubset$cycle, cell_dataSubset$dchV, col="red", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)") - points(cell_dataSubset$cycle, cell_dataSubset$avgV, col="black", main=paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)") - legend("bottomright", c("Charge Voltage","Discharge Voltage","Average Voltage"), col=c("blue","red","black"), pch=19) - dev.off() - } - - # Delta voltage plotting - if (is.element("Delta Voltage", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Delta Voltage Plot.png", sep ="")) - plot(cell_dataSubset$cycle, cell_dataSubset$dV, main=paste("Delta Voltage Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) - dev.off() - } - - # Capacity Loss plotting - if (is.element("Capacity Loss", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row]," Capacity Loss Plot.png", sep ="")) - plot(cell_dataSubset$cycle, cell_dataSubset$lostCap, main=paste("Capacity Loss Plot for", programName, dataSubset$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_dataSubset$lostCap) + (2* sd(cell_dataSubset$lostCap)), mean(cell_dataSubset$lostCap) - (1.5* sd(cell_dataSubset$lostCap)))) - abline(h=median(cell_dataSubset$lostCap), lty="dotted") - dev.off() - } - - if (is.element("dQdV Plots", input$gAnim)) { - dQdVplot <- function(){ - tmp_dataSubset <- dQdVData[dQdVData$cell == row,] - first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2,] - dataSubsetlist <- split(tmp_dataSubset, tmp_dataSubset$cycle) - lapply(dataSubsetlist, function(plotdataSubset){ - p <- plot(plotdataSubset$voltage, plotdataSubset$dQdV, main=paste("dQdV Plot for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", - xlim = c(min(tmp_dataSubset$voltage), max(tmp_dataSubset$voltage)), ylim = c(min(tmp_dataSubset$dQdV), max(tmp_dataSubset$dQdV))) + - points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) - }) - } - save_gif(dQdVplot(), paste(dirLocation, programName, dataSubset$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) + + dQdVData <<- dQdVData[is.finite(dQdVData$voltage), ] + dQdVData <<- dQdVData[is.finite(dQdVData$dQdV), ] + + if (sum(dataSubset$Mass) != 0) { + DCap <- tail(cycle$Q.d, 1) + CCap <- tail(cycle$Q.c, 1) + } else { + DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) + CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) } - - if (is.element("Voltage Profiles", input$gAnim)) { - vpPlot <- function(){ - first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2,] - dataSubsetlist <- split(tmp_excel, tmp_excel$`Cycle_Index`) - lapply(dataSubsetlist, function(plotdataSubset){ - p <- plot(plotdataSubset$CC, plotdataSubset$`Voltage(V)`, main=paste("Voltage Profile for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$`Cycle_Index`[[1]]), xlab=ylabel, ylab= "Voltage (V)", - xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`))) + - points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) - }) - } - save_gif(vpPlot(), paste(dirLocation, programName, dataSubset$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) + + timeCVFracCh <- durations[1] / (durations[1] + durations[2]) + timeCVFracDch <- durations[3] / (durations[3] + durations[4]) + capCVFracCh <- caps[1] / (caps[1] + caps[2]) + capCVFracDch <- caps[3] / (caps[3] + caps[4]) + + if (max(tmp_excel$voltage) > 2) { + CE <- DCap / CCap + } else { + CE <- CCap / DCap } - - # Save all dataSubset within the cell's directory - write.csv(tmp_excel, file = paste(dirLocation, "/", programName,"/", dataSubset$sheet[row],"/", dataSubset$sheet[row],".csv", sep ="")) - - # Append summation dataSubset to the larger dataSubsetsets to be worked with later - final <- rbind(final, tmp_excel) - numCycles <<- rbind(numCycles, data.frame(sheet=dataSubset$sheet[row], cycles=nrow(cell_dataSubset))) - - # Update progress bar - progress$set(value = row, detail = paste("Finished", row," of", nrow(dataSubset)," cells.")) + + # Record charge and discharge voltage, then calculate the delta and average voltage + cycle_facts <<- rbind(cycle_facts, data.frame( + cycle = i, cell = row, chV = chV, dchV = dchV, avgV = (dchV + chV) / 2, + dV = chV - dchV, DCap = DCap, raw_DCap = tail(cycle$"Discharge_Capacity(Ah)", 1), raw_CCap = tail(cycle$"Charge_Capacity(Ah)", 1), CCap = CCap, CE = CE * 100, lostCap = CCap - DCap, cellFade = if (i == 1) { + 0 + } else { + DCap - tail(cycle_facts$DCap, 1) + }, + cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, + capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch + )) + + i <- i + 1 } - + # ###### - # - # Code meant to be run on all dataSubset of all cells should be written here. - # + # + # Code meant to be run on dataSubset"per cell" should be written here + # # ###### - - # With iterations complete, final calculations are being worked - progress$set(detail ="Wrapping up...") - - # Get the last status of each cycle for each cell (namely capacity) - DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) - CE <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(mean) - capSEs <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) - ceSEs <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(se) - stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) - - # Send all the dataSubset to a global variable to be used elsewhere - total <<- final - - tryCatch({ + + cell_dataSubset <- cycle_facts[cycle_facts$cell == row, ] + + # Discharge capacity plotting, with coulombic efficiency being plotted alongside + if (is.element("Discharge Capacity", input$gGraphs)) { + png(paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], " Discharge Capacity Plot.png", sep = "")) + eol <- cell_dataSubset$`DCap`[[1]] * 0.8 + plot(cell_dataSubset$cycle, cell_dataSubset$DCap, type = "p", main = paste("Discharge Capacity for", programName), xlab = NA, ylab = paste("Discharge", ylabel), mai = c(1, 1, 1, 1)) + abline(h = eol, lty = "dotted") + par(new = T) + plot(cell_dataSubset$cycle, cell_dataSubset$CE, type = "p", axes = F, col = "red", ylab = NA, xlab = "Cycle", ylim = c(0, 105)) + mtext(side = 4, line = 3, "Coulombic Efficiency (%)", col = "red") + axis(side = 4, col = "red", col.axis = "red") + dev.off() + } + + # Discharge areal capacity plotting, with coulombic efficiency being plotted alongside + if (is.element("Discharge Areal Capacity", input$gGraphs)) { + png(paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], " Discharge Areal Capacity Plot.png", sep = "")) + new_par <- old_par <- par("mar") + new_par[4] <- old_par[2] + par(mar = new_par) + eol <- ((cell_dataSubset$DCap[[1]] * 1000) / dataSubset$area[row]) * 0.8 + plot(cell_dataSubset$cycle, ((cell_dataSubset$DCap * 1000) / dataSubset$area[row]), type = "p", main = paste("Discharge Areal Capacity for", programName), xlab = NA, ylab = "Discharge Capacity (mAh/cm^2)", mai = c(1, 1, 1, 2)) + abline(h = eol, lty = "dotted") + par(new = T) + plot(cell_dataSubset$cycle, cell_dataSubset$CE, type = "p", axes = F, col = "red", ylab = NA, xlab = "Cycle", ylim = c(0, 105)) + mtext(side = 4, line = 3, "Coulombic Efficiency (%)", col = "red") + axis(side = 4, col = "red", col.axis = "red") + dev.off() + } + + + # Average voltage plotting + if (is.element("Average Voltage", input$gGraphs)) { + png(paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], " Average Voltage Plot.png", sep = "")) + plot(cell_dataSubset$cycle, cell_dataSubset$chV, col = "blue", main = paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab = "Cycle", ylab = "Voltage (V)", ylim = c(min(cell_dataSubset[, 2:4]), max(cell_dataSubset[, 2:4]))) + points(cell_dataSubset$cycle, cell_dataSubset$dchV, col = "red", main = paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab = "Cycle", ylab = "Voltage (V)") + points(cell_dataSubset$cycle, cell_dataSubset$avgV, col = "black", main = paste("Average Voltage Plot for", programName, dataSubset$sheet[row]), xlab = "Cycle", ylab = "Voltage (V)") + legend("bottomright", c("Charge Voltage", "Discharge Voltage", "Average Voltage"), col = c("blue", "red", "black"), pch = 19) + dev.off() + } + + # Delta voltage plotting + if (is.element("Delta Voltage", input$gGraphs)) { + png(paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], " Delta Voltage Plot.png", sep = "")) + plot(cell_dataSubset$cycle, cell_dataSubset$dV, main = paste("Delta Voltage Plot for", programName, dataSubset$sheet[row]), xlab = "Cycle", ylab = "Voltage (V)", ylim = c(0, 0.5)) + dev.off() + } + + # Capacity Loss plotting + if (is.element("Capacity Loss", input$gGraphs)) { + png(paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], " Capacity Loss Plot.png", sep = "")) + plot(cell_dataSubset$cycle, cell_dataSubset$lostCap, main = paste("Capacity Loss Plot for", programName, dataSubset$sheet[row]), xlab = "Cycle", ylab = ylabel, ylim = c(mean(cell_dataSubset$lostCap) + (2 * sd(cell_dataSubset$lostCap)), mean(cell_dataSubset$lostCap) - (1.5 * sd(cell_dataSubset$lostCap)))) + abline(h = median(cell_dataSubset$lostCap), lty = "dotted") + dev.off() + } + + if (is.element("dQdV Plots", input$gAnim)) { + dQdVplot <- function() { + tmp_dataSubset <- dQdVData[dQdVData$cell == row, ] + first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2, ] + dataSubsetlist <- split(tmp_dataSubset, tmp_dataSubset$cycle) + lapply(dataSubsetlist, function(plotdataSubset) { + p <- plot(plotdataSubset$voltage, plotdataSubset$dQdV, + main = paste("dQdV Plot for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$cycle[[1]]), xlab = "Voltage (V)", ylab = "dQdV (Ah/V)", + xlim = c(min(tmp_dataSubset$voltage), max(tmp_dataSubset$voltage)), ylim = c(min(tmp_dataSubset$dQdV), max(tmp_dataSubset$dQdV)) + ) + + points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) + }) + } + save_gif(dQdVplot(), paste(dirLocation, programName, dataSubset$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) + } + + if (is.element("Voltage Profiles", input$gAnim)) { + vpPlot <- function() { + first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2, ] + dataSubsetlist <- split(tmp_excel, tmp_excel$`Cycle_Index`) + lapply(dataSubsetlist, function(plotdataSubset) { + p <- plot(plotdataSubset$CC, plotdataSubset$`Voltage(V)`, + main = paste("Voltage Profile for", programName, dataSubset$sheet[row], "Cycle", plotdataSubset$`Cycle_Index`[[1]]), xlab = ylabel, ylab = "Voltage (V)", + xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`)) + ) + + points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) + }) + } + save_gif(vpPlot(), paste(dirLocation, programName, dataSubset$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) + } + + # Save all dataSubset within the cell's directory + write.csv(tmp_excel, file = paste(dirLocation, "/", programName, "/", dataSubset$sheet[row], "/", dataSubset$sheet[row], ".csv", sep = "")) + + # Append summation dataSubset to the larger dataSubsetsets to be worked with later + final <- rbind(final, tmp_excel) + numCycles <<- rbind(numCycles, data.frame(sheet = dataSubset$sheet[row], cycles = nrow(cell_dataSubset))) + + # Update progress bar + progress$set(value = row, detail = paste("Finished", row, " of", nrow(dataSubset), " cells.")) + } + + # ###### + # + # Code meant to be run on all dataSubset of all cells should be written here. + # + # ###### + + # With iterations complete, final calculations are being worked + progress$set(detail = "Wrapping up...") + + # Get the last status of each cycle for each cell (namely capacity) + DCap <- cycle_facts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + CE <- cycle_facts[c("cycle", "CE")] %>% + group_by(cycle) %>% + summarise_each(mean) + capSEs <- cycle_facts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(se) + ceSEs <- cycle_facts[c("cycle", "CE")] %>% + group_by(cycle) %>% + summarise_each(se) + stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) + + # Send all the dataSubset to a global variable to be used elsewhere + total <<- final + + tryCatch( + { # Total dishcharge capacity plotting if (is.element("Total Discharge Capacity", input$gGraphs)) { - png(paste(dirLocation, "/", programName,"/", "Total Discharge Capacity Plot.png", sep ="")) + png(paste(dirLocation, "/", programName, "/", "Total Discharge Capacity Plot.png", sep = "")) eol <- max(stats$DCap) * 0.8 - plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", programName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) - arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) - abline(h=eol, lty ="dotted") + plot(stats$cycle, stats$DCap, type = "p", main = paste("Discharge Capacity for", programName), xlab = NA, ylab = paste("Discharge", ylabel), mai = c(1, 1, 1, 1)) + arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length = 0.05, angle = 90, code = 3) + abline(h = eol, lty = "dotted") par(new = T) - plot(stats$cycle, stats$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) - arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length=0.05, angle=90, code=3, col ="red") - axis(side = 4, col ="red") - mtext(side = 4, line = 2,"Coulombic Efficiency (%)") + plot(stats$cycle, stats$CE, type = "p", axes = F, col = "red", ylab = NA, xlab = "Cycle", ylim = c(0, 105)) + arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length = 0.05, angle = 90, code = 3, col = "red") + axis(side = 4, col = "red") + mtext(side = 4, line = 2, "Coulombic Efficiency (%)") dev.off() } - }, error = function(cond) { + }, + error = function(cond) { print(cond) - }) - - # Save total dataSubset and stats - write.csv(stats, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Summary.csv", sep ="")) - if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," dQdV data.csv", sep ="")) - write.csv(cycle_facts, file = paste(dirLocation, "/", programName,"/", basename(dirLocation)," Cycle Facts.csv", sep ="")) - - # If a histor directory does not exist, create it. Save all the dataSubset revelant to plotting to a RdataSubset file. - if (!dir.exists(paste(dirLocation, "history", sep = "/"))) { - dir.create(paste(dirLocation, "history", sep = "/")) } - - dirName <<- programName - data <- dataSubset - - save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".Rdata", sep = "")) - } - - # Modal for completed analysis - shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation, "/", programName, sep = ""), - type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", - callbackR = function(x) { - if (x) { - export_to_origin() - } - } ) - - # Finish progress bar - progress$set(value = nrow(data)) - - # Re-enable all input fields, including graph builder - enable("files") - enable("lowV") - enable("highV") - enable("dirLocation") - enable("submit") - enable("excelImport") - enable("gGraphs") - enable("peakFit") - enable("area") - enable("perActive") - enable("capActive") - enable("graphBuilder") - - - for (num in 1:size(groupList)[2]) { - # Close progress bar - progress$close() + + # Save total dataSubset and stats + write.csv(stats, file = paste(dirLocation, "/", programName, "/", basename(dirLocation), " Summary.csv", sep = "")) + if (!arbinCM) write.csv(dQdVData, file = paste(dirLocation, "/", programName, "/", basename(dirLocation), " dQdV data.csv", sep = "")) + write.csv(cycle_facts, file = paste(dirLocation, "/", programName, "/", basename(dirLocation), " Cycle Facts.csv", sep = "")) + + # If a histor directory does not exist, create it. Save all the dataSubset revelant to plotting to a RdataSubset file. + if (!dir.exists(paste(dirLocation, "history", sep = "/"))) { + dir.create(paste(dirLocation, "history", sep = "/")) } + + dirName <<- programName + data <- dataSubset + + save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation, "/history/", programName, ".Rdata", sep = "")) } - - # ###### - # - # Graph Builder Processing - # - # ###### - output$outputPlot <- renderPlot({ - + + # Modal for completed analysis + shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation, "/", programName, sep = ""), + type = "success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", + callbackR = function(x) { + if (x) { + export_to_origin() + } + } + ) + + # Finish progress bar + progress$set(value = nrow(data)) + + # Re-enable all input fields, including graph builder + enable("files") + enable("lowV") + enable("highV") + enable("dirLocation") + enable("submit") + enable("excelImport") + enable("gGraphs") + enable("peakFit") + enable("area") + enable("perActive") + enable("capActive") + enable("graphBuilder") + + + for (num in 1:size(groupList)[2]) { + # Close progress bar + progress$close() + } + } + + # ###### + # + # Graph Builder Processing + # + # ###### + output$outputPlot <- renderPlot( + { tmp_data <<- data.frame() normalTime <<- data.frame() - + bounds <<- c(input$xMin, input$xMax, input$yMin, input$yMax) - + # Define function to normalize Voltage vs. Time plots normalizeTime <- function(x) { return(x - x[[1]]) } - + # ###### - # + # # Switch statements defining the bulk of the processing, depending on the desired graph - # + # # ###### - if (input$perType =="Within Analysis") { + if (input$perType == "Within Analysis") { sheetName <<- TRUE - + # Get the indicies in which the desired cells are in the data frame containing the number of cycles cellIndex <- match(input$cells, numCycles$sheet) - + switch(input$typeGraph, - "dQdV Graphs" = { - tmp_data <<- data.frame(x=dQdVData[dQdVData$cell %in% cellIndex,]$voltage, y=dQdVData[dQdVData$cell %in% cellIndex,]$dQdV, cycle=dQdVData[dQdVData$cell %in% cellIndex,]$cycle, cell=dQdVData[dQdVData$cell %in% cellIndex,]$cell) - tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)),] - - titleLabel <<-"dQdV Plot" - xlabel <<-"Voltage (V)" - ylabel <<-"dQdV (mAh/V)" - }, - "Voltage Profiles" = { - tmp_data <<- data.frame(x = total[total$Cell %in% cellIndex,]$CC, y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) - tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)),] - - titleLabel <<-"Voltage Profile" - if (sum(data$Mass) != 0) { - xlabel <-"Continuous Capacity (mAh/g)" - } else { - xlabel <-"Continuous Capacity (Ah)" - } - ylabel <<-"Voltage (V)" - }, - "Voltage vs. Time" = { - tmp_data <<- data.frame(x=(total[total$Cell %in% cellIndex,]$`Test_Time(s)` / 60), y=total[total$Cell %in% cellIndex,]$`Voltage(V)`, cycle=total[total$Cell %in% cellIndex,]$`Cycle_Index`, cell=total[total$Cell %in% cellIndex,]$Cell) - tmp_data <<- tmp_data[tmp_data$cycle %in% input$renderCycles,] - - x <- 0 - - for (cell in cellIndex) { - normalTime <<- c(normalTime, t(aggregate(tmp_data[tmp_data$cell == cell,]$x, by=list(tmp_data[tmp_data$cell == cell,]$cycle), normalizeTime)[,2])) - } - - tmp_data <<- data.frame(x=unlist(normalTime), y=tmp_data$y, cycle=tmp_data$cycle, cell=tmp_data$cell) - tmp_data <<- tmp_data[tmp_data$y >= 0.01,] - - titleLabel <<-"Voltge vs. Time Plot" - xlabel <<-"Time (min)" - ylabel <<-"Voltage (V)" - }, - "Charge Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$chV, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, cell = cycle_facts[cycle_facts$cell %in% cellIndex,]$cell) - - titleLabel <<- "Charge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Discharge Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dchV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Discharge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Average Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$avgV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Average Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Delta Voltage" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$dV, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Delta Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - "Discharge Capacity" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$DCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Discharge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Discharge Capacity (mAh/g)" - } else { - ylabel <<- "Discharge Capacity (Ah)" - } - }, - "Charge Capacity" = { - tmp_data <<- data.frame(x=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle, y=cycle_facts[cycle_facts$cell %in% cellIndex,]$CCap, cell=cycle_facts[cycle_facts$cell %in% cellIndex,]$cell, cycle=cycle_facts[cycle_facts$cell %in% cellIndex,]$cycle) - - titleLabel <<- "Charge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Charge Capacity (mAh/g)" - } else { - ylabel <<- "Charge Capacity (Ah)" - } - } + "dQdV Graphs" = { + tmp_data <<- data.frame(x = dQdVData[dQdVData$cell %in% cellIndex, ]$voltage, y = dQdVData[dQdVData$cell %in% cellIndex, ]$dQdV, cycle = dQdVData[dQdVData$cell %in% cellIndex, ]$cycle, cell = dQdVData[dQdVData$cell %in% cellIndex, ]$cell) + tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)), ] + + titleLabel <<- "dQdV Plot" + xlabel <<- "Voltage (V)" + ylabel <<- "dQdV (mAh/V)" + }, + "Voltage Profiles" = { + tmp_data <<- data.frame(x = total[total$Cell %in% cellIndex, ]$CC, y = total[total$Cell %in% cellIndex, ]$`Voltage(V)`, cycle = total[total$Cell %in% cellIndex, ]$`Cycle_Index`, cell = total[total$Cell %in% cellIndex, ]$Cell) + tmp_data <<- tmp_data[tmp_data$cycle == sort(as.numeric(input$renderCycles)), ] + + titleLabel <<- "Voltage Profile" + if (sum(data$Mass) != 0) { + xlabel <- "Continuous Capacity (mAh/g)" + } else { + xlabel <- "Continuous Capacity (Ah)" + } + ylabel <<- "Voltage (V)" + }, + "Voltage vs. Time" = { + tmp_data <<- data.frame(x = (total[total$Cell %in% cellIndex, ]$`Test_Time(s)` / 60), y = total[total$Cell %in% cellIndex, ]$`Voltage(V)`, cycle = total[total$Cell %in% cellIndex, ]$`Cycle_Index`, cell = total[total$Cell %in% cellIndex, ]$Cell) + tmp_data <<- tmp_data[tmp_data$cycle %in% input$renderCycles, ] + + x <- 0 + + for (cell in cellIndex) { + normalTime <<- c(normalTime, t(aggregate(tmp_data[tmp_data$cell == cell, ]$x, by = list(tmp_data[tmp_data$cell == cell, ]$cycle), normalizeTime)[, 2])) + } + + tmp_data <<- data.frame(x = unlist(normalTime), y = tmp_data$y, cycle = tmp_data$cycle, cell = tmp_data$cell) + tmp_data <<- tmp_data[tmp_data$y >= 0.01, ] + + titleLabel <<- "Voltge vs. Time Plot" + xlabel <<- "Time (min)" + ylabel <<- "Voltage (V)" + }, + "Charge Voltage" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$chV, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell) + + titleLabel <<- "Charge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Discharge Voltage" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$dchV, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle) + + titleLabel <<- "Discharge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Average Voltage" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$avgV, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle) + + titleLabel <<- "Average Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Delta Voltage" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$dV, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle) + + titleLabel <<- "Delta Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + "Discharge Capacity" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$DCap, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle) + + titleLabel <<- "Discharge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Discharge Capacity (mAh/g)" + } else { + ylabel <<- "Discharge Capacity (Ah)" + } + }, + "Charge Capacity" = { + tmp_data <<- data.frame(x = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle, y = cycle_facts[cycle_facts$cell %in% cellIndex, ]$CCap, cell = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cell, cycle = cycle_facts[cycle_facts$cell %in% cellIndex, ]$cycle) + + titleLabel <<- "Charge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Charge Capacity (mAh/g)" + } else { + ylabel <<- "Charge Capacity (Ah)" + } + } ) - - tmp_data$color <<- sapply(tmp_data$cycle, function(x) {match(x, input$renderCycles, nomatch = 1)}) - tmp_data$symbol <<- sapply(tmp_data$cell, function(x) {match(x, cellIndex)}) - + + tmp_data$color <<- sapply(tmp_data$cycle, function(x) { + match(x, input$renderCycles, nomatch = 1) + }) + tmp_data$symbol <<- sapply(tmp_data$cell, function(x) { + match(x, cellIndex) + }) } else if (input$perType == "Between Analyses") { - switch(input$typeGraph, - "Charge Voltage" = { - x<-0 - chV <- cycle_facts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(mean) - chVSE <- cycle_facts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_chV <- compCycleFacts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_chVSE <- compCycleFacts[c("cycle","chV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(chV$cycle, comp_chV$cycle), y=c(chV$chV, comp_chV$chV), se=c(chVSE$chV, comp_chVSE$chV), cell=c(rep(1, length(chV$cycle)), rep(2, length(comp_chV$cycle))), cycle=c(chV$cycle, comp_chV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Charge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) - }, - "Discharge Voltage" = { - x<-0 - dchV <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) - dchVSE <- cycle_facts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_dchV <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_dchVSE <- compCycleFacts[c("cycle","dchV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(dchV$cycle, comp_dchV$cycle), y=c(dchV$dchV, comp_dchV$dchV), se=c(dchVSE$dchV, comp_dchVSE$dchV), cell=c(rep(1, length(dchV$cycle)), rep(2, length(comp_dchV$cycle))), cycle=c(dchV$cycle, comp_dchV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Disharge Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) - }, - "Average Voltage" = { - avgV <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) - avgVSE <- cycle_facts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_avgV <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_avgVSE <- compCycleFacts[c("cycle","avgV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(avgV$cycle, comp_avgV$cycle), y=c(avgV$avgV, comp_avgV$avgV), se=c(avgVSE$avgV, comp_avgVSE$avgV), cell=c(rep(1, length(avgV$cycle)), rep(2, length(comp_avgV$cycle))), cycle=c(avgV$cycle, comp_avgV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Average Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) - }, - "Delta Voltage" = { - dV <- cycle_facts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(mean) - dVSE <- cycle_facts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_dV <- compCycleFacts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(mean) - comp_dVSE <- compCycleFacts[c("cycle","dV")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(dV$cycle, comp_dV$cycle), y=c(dV$dV, comp_dV$dV), se=c(dVSE$dV, comp_dVSE$dV), cell=c(rep(1, length(dV$cycle)), rep(2, length(comp_dV$cycle))), cycle=c(dV$cycle, comp_dV$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Delta Voltage Plot " - xlabel <<- "Cycle" - ylabel <<- "Voltage (V)" - }, - error = function(x) { - print(x) - }) - }, - "Discharge Capacity" = { - DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) - DCapSE <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_DCap <- compCycleFacts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) - comp_DCapSE <- compCycleFacts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(DCap$cycle, comp_DCap$cycle), y=c(DCap$DCap, comp_DCap$DCap), se=c(DCapSE$DCap, comp_DCapSE$DCap), cell=c(rep(1, length(DCap$cycle)), rep(2, length(comp_DCap$cycle))), cycle=c(DCap$cycle, comp_DCap$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Discharge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Discharge Capacity (mAh/g)" - } else { - ylabel <<- "Discharge Capacity (Ah)" - } - }, - error = function(x) { - print(x) - }) - }, - "Charge Capacity" = { - CCap <- cycle_facts[c("cycle","CCap")] %>% group_by(cycle) %>% summarise_each(mean) - CCapSE <- cycle_facts[c("cycle","CCap")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_CCap <- compCycleFacts[c("cycle","CCap")] %>% group_by(cycle) %>% summarise_each(mean) - comp_CCapSE <- compCycleFacts[c("cycle","CCap")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(CCap$cycle, comp_CCap$cycle), y=c(CCap$CCap, comp_CCap$CCap), se=c(CCapSE$CCap, comp_CCapSE$CCap), cell=c(rep(1, length(CCap$cycle)), rep(2, length(comp_CCap$cycle))), cycle=c(CCap$cycle, comp_CCap$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Charge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Charge Capacity (mAh/g)" - } else { - ylabel <<- "Charge Capacity (Ah)" - } - }, - error = function(x) { - print(x) - }) - }, - "Capacity Loss" = { - lostCap <- cycle_facts[c("cycle","lostCap")] %>% group_by(cycle) %>% summarise_each(mean) - lostCapSE <- cycle_facts[c("cycle","lostCap")] %>% group_by(cycle) %>% summarise_each(se) - - tryCatch({ - comp_lostCap <- compCycleFacts[c("cycle","lostCap")] %>% group_by(cycle) %>% summarise_each(mean) - comp_lostCapSE <- compCycleFacts[c("cycle","lostCap")] %>% group_by(cycle) %>% summarise_each(se) - - tmp_data <<- data.frame(x=c(lostCap$cycle, comp_lostCap$cycle), y=c(lostCap$lostCap, comp_lostCap$lostCap), se=c(lostCapSE$lostCap, comp_lostCapSE$lostCap), cell=c(rep(1, length(lostCap$cycle)), rep(2, length(comp_lostCap$cycle))), cycle=c(lostCap$cycle, comp_lostCap$cycle)) - tmp_data$symbol <<- rep(1, nrow(tmp_data)) - tmp_data$color <<- sapply(tmp_data$cell, function(x) {match(x, c(1,2))}) - - titleLabel <<- "Charge Capacity Plot " - xlabel <<- "Cycle" - if (sum(data$Mass) != 0) { - ylabel <<- "Capacity (mAh/g)" - } else { - ylabel <<- "Capacity (Ah)" - } - }, - error = function(x) { - print(x) - }) - }, + "Charge Voltage" = { + x <- 0 + chV <- cycle_facts[c("cycle", "chV")] %>% + group_by(cycle) %>% + summarise_each(mean) + chVSE <- cycle_facts[c("cycle", "chV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_chV <- compCycleFacts[c("cycle", "chV")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_chVSE <- compCycleFacts[c("cycle", "chV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(chV$cycle, comp_chV$cycle), y = c(chV$chV, comp_chV$chV), se = c(chVSE$chV, comp_chVSE$chV), cell = c(rep(1, length(chV$cycle)), rep(2, length(comp_chV$cycle))), cycle = c(chV$cycle, comp_chV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Charge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + error = function(x) { + print(x) + } + ) + }, + "Discharge Voltage" = { + x <- 0 + dchV <- cycle_facts[c("cycle", "dchV")] %>% + group_by(cycle) %>% + summarise_each(mean) + dchVSE <- cycle_facts[c("cycle", "dchV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_dchV <- compCycleFacts[c("cycle", "dchV")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_dchVSE <- compCycleFacts[c("cycle", "dchV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(dchV$cycle, comp_dchV$cycle), y = c(dchV$dchV, comp_dchV$dchV), se = c(dchVSE$dchV, comp_dchVSE$dchV), cell = c(rep(1, length(dchV$cycle)), rep(2, length(comp_dchV$cycle))), cycle = c(dchV$cycle, comp_dchV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Disharge Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + error = function(x) { + print(x) + } + ) + }, + "Average Voltage" = { + avgV <- cycle_facts[c("cycle", "avgV")] %>% + group_by(cycle) %>% + summarise_each(mean) + avgVSE <- cycle_facts[c("cycle", "avgV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_avgV <- compCycleFacts[c("cycle", "avgV")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_avgVSE <- compCycleFacts[c("cycle", "avgV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(avgV$cycle, comp_avgV$cycle), y = c(avgV$avgV, comp_avgV$avgV), se = c(avgVSE$avgV, comp_avgVSE$avgV), cell = c(rep(1, length(avgV$cycle)), rep(2, length(comp_avgV$cycle))), cycle = c(avgV$cycle, comp_avgV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Average Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + error = function(x) { + print(x) + } + ) + }, + "Delta Voltage" = { + dV <- cycle_facts[c("cycle", "dV")] %>% + group_by(cycle) %>% + summarise_each(mean) + dVSE <- cycle_facts[c("cycle", "dV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_dV <- compCycleFacts[c("cycle", "dV")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_dVSE <- compCycleFacts[c("cycle", "dV")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(dV$cycle, comp_dV$cycle), y = c(dV$dV, comp_dV$dV), se = c(dVSE$dV, comp_dVSE$dV), cell = c(rep(1, length(dV$cycle)), rep(2, length(comp_dV$cycle))), cycle = c(dV$cycle, comp_dV$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Delta Voltage Plot " + xlabel <<- "Cycle" + ylabel <<- "Voltage (V)" + }, + error = function(x) { + print(x) + } + ) + }, + "Discharge Capacity" = { + DCap <- cycle_facts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + DCapSE <- cycle_facts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_DCap <- compCycleFacts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_DCapSE <- compCycleFacts[c("cycle", "DCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(DCap$cycle, comp_DCap$cycle), y = c(DCap$DCap, comp_DCap$DCap), se = c(DCapSE$DCap, comp_DCapSE$DCap), cell = c(rep(1, length(DCap$cycle)), rep(2, length(comp_DCap$cycle))), cycle = c(DCap$cycle, comp_DCap$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Discharge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Discharge Capacity (mAh/g)" + } else { + ylabel <<- "Discharge Capacity (Ah)" + } + }, + error = function(x) { + print(x) + } + ) + }, + "Charge Capacity" = { + CCap <- cycle_facts[c("cycle", "CCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + CCapSE <- cycle_facts[c("cycle", "CCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_CCap <- compCycleFacts[c("cycle", "CCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_CCapSE <- compCycleFacts[c("cycle", "CCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(CCap$cycle, comp_CCap$cycle), y = c(CCap$CCap, comp_CCap$CCap), se = c(CCapSE$CCap, comp_CCapSE$CCap), cell = c(rep(1, length(CCap$cycle)), rep(2, length(comp_CCap$cycle))), cycle = c(CCap$cycle, comp_CCap$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Charge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Charge Capacity (mAh/g)" + } else { + ylabel <<- "Charge Capacity (Ah)" + } + }, + error = function(x) { + print(x) + } + ) + }, + "Capacity Loss" = { + lostCap <- cycle_facts[c("cycle", "lostCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + lostCapSE <- cycle_facts[c("cycle", "lostCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tryCatch( + { + comp_lostCap <- compCycleFacts[c("cycle", "lostCap")] %>% + group_by(cycle) %>% + summarise_each(mean) + comp_lostCapSE <- compCycleFacts[c("cycle", "lostCap")] %>% + group_by(cycle) %>% + summarise_each(se) + + tmp_data <<- data.frame(x = c(lostCap$cycle, comp_lostCap$cycle), y = c(lostCap$lostCap, comp_lostCap$lostCap), se = c(lostCapSE$lostCap, comp_lostCapSE$lostCap), cell = c(rep(1, length(lostCap$cycle)), rep(2, length(comp_lostCap$cycle))), cycle = c(lostCap$cycle, comp_lostCap$cycle)) + tmp_data$symbol <<- rep(1, nrow(tmp_data)) + tmp_data$color <<- sapply(tmp_data$cell, function(x) { + match(x, c(1, 2)) + }) + + titleLabel <<- "Charge Capacity Plot " + xlabel <<- "Cycle" + if (sum(data$Mass) != 0) { + ylabel <<- "Capacity (mAh/g)" + } else { + ylabel <<- "Capacity (Ah)" + } + }, + error = function(x) { + print(x) + } + ) + }, ) } - - tmp_data <<- tmp_data[is.finite(tmp_data$x),] - tmp_data <<- tmp_data[is.finite(tmp_data$y),] - tmp_data <<- tmp_data[is.finite(tmp_data$cycle),] - tmp_data <<- tmp_data[is.finite(tmp_data$cell),] - + + tmp_data <<- tmp_data[is.finite(tmp_data$x), ] + tmp_data <<- tmp_data[is.finite(tmp_data$y), ] + tmp_data <<- tmp_data[is.finite(tmp_data$cycle), ] + tmp_data <<- tmp_data[is.finite(tmp_data$cell), ] + if (any(sapply(bounds, is.na))) { if (is.na(bounds[1])) bounds[1] <<- min(tmp_data$x) if (is.na(bounds[2])) bounds[2] <<- max(tmp_data$x) if (is.na(bounds[3])) bounds[3] <<- min(tmp_data$y) if (is.na(bounds[4])) bounds[4] <<- max(tmp_data$y) } - - tryCatch({ - if (input$plotStyle =="o" | input$plotStyle =="p" | "se" %in% colnames(tmp_data)) { - par(mar=c(5.1, 6.1, 4.1, 2.1)) - - plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) - if ("se" %in% colnames(tmp_data)) { - arrows(tmp_data$x, tmp_data$y - tmp_data$se, tmp_data$x, tmp_data$y + tmp_data$se, col = tmp_data$color, length=0.05, angle=90, code=3) - #legend("bottomright", legend = c(input$originalData, input$compareData), col = c(1,2), pch = 19) - } else { - if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { - #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + + tryCatch( + { + if (input$plotStyle == "o" | input$plotStyle == "p" | "se" %in% colnames(tmp_data)) { + par(mar = c(5.1, 6.1, 4.1, 2.1)) + + plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main = titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab = xlabel, ylab = ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) + if ("se" %in% colnames(tmp_data)) { + arrows(tmp_data$x, tmp_data$y - tmp_data$se, tmp_data$x, tmp_data$y + tmp_data$se, col = tmp_data$color, length = 0.05, angle = 90, code = 3) + # legend("bottomright", legend = c(input$originalData, input$compareData), col = c(1,2), pch = 19) } else { - #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(1:length(input$cells)), title ="Cycle", ncol=2) + if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { + # legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + } else { + # legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(1:length(input$cells)), title ="Cycle", ncol=2) + } } - } - } else if (input$plotStyle =="l") { - newLine <- subset(tmp_data, tmp_data$color == 1 & tmp_data$symbol == 1) - plot(newLine$x, newLine$y, type ="l", col = newLine$color, lty = newLine$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, lwd = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) - - for (i in 1:length(input$renderCycles)) { - for (n in 1:length(cellIndex)) { - newLine <- subset(tmp_data, tmp_data$color == i & tmp_data$symbol == n) - lines(newLine$x, newLine$y, col = newLine$color, lty = newLine$symbol, lwd = input$pointSize) + } else if (input$plotStyle == "l") { + newLine <- subset(tmp_data, tmp_data$color == 1 & tmp_data$symbol == 1) + plot(newLine$x, newLine$y, type = "l", col = newLine$color, lty = newLine$symbol, main = titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab = xlabel, ylab = ylabel, lwd = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) + + for (i in 1:length(input$renderCycles)) { + for (n in 1:length(cellIndex)) { + newLine <- subset(tmp_data, tmp_data$color == i & tmp_data$symbol == n) + lines(newLine$x, newLine$y, col = newLine$color, lty = newLine$symbol, lwd = input$pointSize) + } + } + if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { + legend("topright", legend = c(sort(as.numeric(input$renderCycles)), if ("se" %in% colnames(tmp_data)) { + c(input$dirName, compName) + }), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title = "Cycle", ncol = 2) + } else { + legend("topright", legend = c(sort(as.numeric(input$renderCycles)), input$cells, if ("se" %in% colnames(tmp_data)) { + c(input$dirName, compName) + }), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(1:length(input$cells)), title = "Cycle", ncol = 2) } } - if (input$typeGraph %in% c("dQdV Graphs", "Voltage Profiles", "Voltage vs. Time")) { - legend("topright", legend = c(sort(as.numeric(input$renderCycles)), if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + }, + error = function(cond) { + if (length(input$cells) != 0) { + text(0.5, 0.5, labels = "This graph requires you to select a cycle!", cex = 2) } else { - legend("topright", legend = c(sort(as.numeric(input$renderCycles)), input$cells, if ("se" %in% colnames(tmp_data)) {c(input$dirName, compName)}), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(1:length(input$cells)), title ="Cycle", ncol=2) + text(0.5, 0.5, labels = "You don messed up A-aron!\n (no data to plot)", cex = 2) } + print(cond) + return(NA) } - }, - error=function(cond) { - if (length(input$cells) != 0) { - text(0.5, 0.5, labels ="This graph requires you to select a cycle!", cex = 2) - } else { - text(0.5, 0.5, labels ="You don messed up A-aron!\n (no data to plot)", cex = 2) - } - print(cond) - return(NA) - }) - }, res = 125) - - # Enable/Disable input field based on desired grph selection - observeEvent(input$gGraphs, { - disable("area") - disable("perActive") - disable("capActive") - - choices <- c("dQdV Graphs","Voltage Profiles","Voltage vs. Time","Discharge Capacity","Discharge Areal Capacity", - "Total Discharge Capacity","Average Voltage","Delta Voltage") - - if (is.element("Discharge Areal Capacity", input$gGraphs)) { - enable("area") - } + ) + }, + res = 125 + ) + + # Enable/Disable input field based on desired grph selection + observeEvent(input$gGraphs, { + disable("area") + disable("perActive") + disable("capActive") + + choices <- c( + "dQdV Graphs", "Voltage Profiles", "Voltage vs. Time", "Discharge Capacity", "Discharge Areal Capacity", + "Total Discharge Capacity", "Average Voltage", "Delta Voltage" + ) + + if (is.element("Discharge Areal Capacity", input$gGraphs)) { + enable("area") + } + }) + + # Method for handling changes in cell selection + observeEvent(input$cells, { + tmp_cycles <<- input$renderCycles + updateSelectInput(session, "renderCycles", choices = 1:max(numCycles$cycles), selected = tmp_cycles) + }) + + observeEvent(input$compAnalysis, { + load(input$compAnalysis$datapath[[1]]) + + compName <<- basename(input$compAnalysis$datapath[[1]]) + compCycleFacts <<- cycle_facts + + sheetName <<- !sheetName + }) + + # Error handling for graphBuilder and then showing modal + observeEvent(input$graphBuilder, { + if (dim(numCycles)[1] == 0 | dim(dQdVData)[1] == 0 | dim(cycle_facts)[1] == 0) { + shinyalert("No Data!", "Please run the analysis first or load a previous environment.", "error") + } else { + updateCheckboxGroupInput(session, "cells", choices = data$sheet) + showModal(graphbuilder) + } + }) + + observeEvent(input$plot_click, { + output$hoverCoordx <- renderText({ + input$plot_click$x }) - - # Method for handling changes in cell selection - observeEvent(input$cells, { - tmp_cycles <<- input$renderCycles - updateSelectInput(session,"renderCycles", choices = 1:max(numCycles$cycles), selected = tmp_cycles) + output$hoverCoordy <- renderText({ + input$plot_click$y }) - - observeEvent(input$compAnalysis, { - load(input$compAnalysis$datapath[[1]]) - - compName <<- basename(input$compAnalysis$datapath[[1]]) - compCycleFacts <<- cycle_facts - + }) + + observeEvent(input$perType, { + if (input$perType == "Within Analysis") { + updateRadioButtons(session, "typeGraph", choices = c( + "dQdV Graphs", "Voltage Profiles", "Voltage vs. Time", + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" + )) + + show("cells") + show("renderCycles") + show("plotStyle") + hide("originalData") + hide("compareData") + hide("compAnalysis") + hide("analysis") + } else if (input$perType == "Between Analyses") { + updateRadioButtons(session, "typeGraph", choices = c( + "Charge Voltage", "Discharge Voltage", + "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity", + "Capacity Loss" + )) + sheetName <<- !sheetName - }) - - # Error handling for graphBuilder and then showing modal - observeEvent(input$graphBuilder, { - if(dim(numCycles)[1] == 0 | dim(dQdVData)[1] == 0 | dim(cycle_facts)[1] == 0) { - shinyalert("No Data!","Please run the analysis first or load a previous environment.","error") - } else { - updateCheckboxGroupInput(session,"cells", choices = data$sheet) - showModal(graphbuilder) - } - }) - - observeEvent(input$plot_click, { - output$hoverCoordx <- renderText({input$plot_click$x}) - output$hoverCoordy <- renderText({input$plot_click$y}) - }) - - observeEvent(input$perType, { - if (input$perType == "Within Analysis") { - updateRadioButtons(session, "typeGraph", choices = c("dQdV Graphs","Voltage Profiles", "Voltage vs. Time", - "Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity" )) - - show("cells") - show("renderCycles") - show("plotStyle") - hide("originalData") - hide("compareData") - hide("compAnalysis") - hide("analysis") - } else if (input$perType == "Between Analyses") { - updateRadioButtons(session, "typeGraph", choices = c("Charge Voltage", "Discharge Voltage", - "Average Voltage", "Delta Voltage", "Discharge Capacity", "Charge Capacity", - "Capacity Loss")) - - sheetName <<- !sheetName - - hide("cells") - hide("renderCycles") - show("originalData") - show("compareData") - hide("plotStyle") - show("compAnalysis") - show("analysis") - } - }) - - # Method for saving graph generated by graphBuilder - observeEvent(input$saveGraph, { - png(paste(input$fileName,".png"), res = 125) - - if (input$plotStyle =="o" | input$plotStyle =="p") { - plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) - #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) - } else if (input$plotStyle =="l") { - newLine <- subset(tmp_data, tmp_data$color == 1 & tmp_data$symbol == 1) - plot(newLine$x, newLine$y, type ="l", col = newLine$color, lty = newLine$symbol, main=titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab=xlabel, ylab=ylabel, lwd = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) - - for (i in 1:length(input$renderCycles)) { - for (n in 1:length(cellIndex)) { - newLine <- subset(tmp_data, tmp_data$color == i & tmp_data$symbol == n) - lines(newLine$x, newLine$y, col = newLine$color, lty = newLine$symbol, lwd = input$pointSize) - } + + hide("cells") + hide("renderCycles") + show("originalData") + show("compareData") + hide("plotStyle") + show("compAnalysis") + show("analysis") + } + }) + + # Method for saving graph generated by graphBuilder + observeEvent(input$saveGraph, { + png(paste(input$fileName, ".png"), res = 125) + + if (input$plotStyle == "o" | input$plotStyle == "p") { + plot(tmp_data$x, tmp_data$y, type = input$plotStyle, col = tmp_data$color, pch = tmp_data$symbol, main = titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab = xlabel, ylab = ylabel, cex = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) + # legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), pch = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + } else if (input$plotStyle == "l") { + newLine <- subset(tmp_data, tmp_data$color == 1 & tmp_data$symbol == 1) + plot(newLine$x, newLine$y, type = "l", col = newLine$color, lty = newLine$symbol, main = titleLabel, xlim = c(bounds[1], bounds[2]), ylim = c(bounds[3], bounds[4]), xlab = xlabel, ylab = ylabel, lwd = input$pointSize, cex.axis = input$textSize, cex.lab = input$textSize, cex.main = input$textSize) + + for (i in 1:length(input$renderCycles)) { + for (n in 1:length(cellIndex)) { + newLine <- subset(tmp_data, tmp_data$color == i & tmp_data$symbol == n) + lines(newLine$x, newLine$y, col = newLine$color, lty = newLine$symbol, lwd = input$pointSize) } - - #legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) } - - dev.off() - - shinyalert("Success!", paste("Plot saved in working directory:\n", getwd()),"success") - }) -} \ No newline at end of file + + # legend("bottomright", legend = c(sort(as.numeric(input$renderCycles)), input$cells), col = c(unique(tmp_data$color), rep("black", length(input$cells))), lty = c(rep(19, length(unique(tmp_data$color))), 1:length(input$cells)), title ="Cycle", ncol=2) + } + + dev.off() + + shinyalert("Success!", paste("Plot saved in working directory:\n", getwd()), "success") + }) +} diff --git a/ui.R b/ui.R index e6a9a0b..11fa0d4 100644 --- a/ui.R +++ b/ui.R @@ -2,110 +2,110 @@ library(shiny) ui <- shinyUI({ fluidPage( - - # Utilizing javascript within Shiny allows for added features such as enable/disable of inputs - useShinyjs(), - - # Shinyalert is a package that makes interactive"pop-ups" (modals) easy to generate - useShinyalert(), - - setBackgroundColor( - color = c("ghostwhite", "lightgrey"), - gradient = "linear", - direction = "bottom", - shinydashboard = FALSE - ), - - fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), - - fluidRow("Analysis Mode", - switchInput("batchProcessing", onLabel = "Batch Processing", offLabel = "Single Analysis", handleWidth = "200px") - , align = "center", style = "margin: 0%;"), - - # This first column is where most user inputs are, with the exception of the directory name - column(4, align = "center", - - # This generates the optional block in which the user can import a previous R environment - fluidRow( - strong("Start Here"), tags$br(), - "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), - actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width: 80%; margin: 5%; font-size: 100%"), tags$br(), - helpText("The analysis will create a folder within the selected folder."), - style = "border: 1px solid black; padding: 5%; margin: 5%;" - ), - - fluidRow( - fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), - actionButton("load", "Load"), - style = "border: 1px dashed black; margin: 5%; padding: 5%;" - ), - - # These are the"optional" parameters that need to be filled out if select graphs are selected - fluidRow( - strong("Optional Parameters"), tags$br(), - "Parameters responsible for certain graphs.", tags$br(), tags$br(), - - # Used for dishcharge areal capacity graphs - numericInput("area","Limiting Electrode Area (cm^2)", 2.74, min = 0), - - # Used for C-Rate calculations - # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), - # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), - style ="border: 1px dashed black; padding: 5%; margin: 5%;" - ), - ), - - # The second column is where selection of graphs and further features are selected - column(4, align ="left", - fluidRow( - # Presents options for graphs to be generated - "Choose graphs to be generated: ", - actionButton("whatGraph","What's this?", class ="btn-link"), - checkboxGroupInput("gGraphs", NULL, choices = c("Discharge Capacity","Discharge Areal Capacity", - "Total Discharge Capacity","Average Voltage","Delta Voltage","Capacity Loss"), inline = FALSE), - "Choose graphs to animate: ", - checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), - # "Advanced Analysis", - # radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), - # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), - style ="margin: 5%; border: 1px solid black; padding: 5%;" - ), - ), - - # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process - column(4, align ="center", - fluidRow( - selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "HighRate", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), - strong("Files to be Analyzed*"), tags$br(), - "Import all Arbin files of interest.", tags$br(), tags$br(), - fileInput("files", NULL, multiple = TRUE), - actionButton("submit", "Begin Analysis", class = 'btn-success', style = "width: 80%; height: 100px; margin: 5%; font-size: 100%;"), - actionButton("clearTable", "Clear Cells", class = 'btn-danger', style = "width: 60%; margin: 5%; font-size: 100%;"), - style = "border: 4px double black; padding: 5%; margin: 5%;" - ), - - # This final block enables a button after data becomes available, which trigger the modal to build custom graphs - fluidRow( - strong("Custom Graph Builder"), tags$br(), - "Customize Graphs Once Data is Available", - disabled(actionButton("graphBuilder","Launch", width = '80%', class ="btn-primary", style ="height: 50px; margin: 5%; font-size: 100%;")), tags$br(), - style ="border: 1px solid black; padding: 5%; margin: 5%" - ), - - fluidRow( - strong("Optional: Import Active Material Masses from Excel"), tags$br(), - "Running Analysis without Masses Will Render Raw Capacities (Ah)", - textAreaInput("masses", NULL, height = "100px", resize = "vertical"), - helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), - actionButton("excelImport", "Import"), - style = "border: 1px dashed black; padding: 5%; margin: 5%;" - ), - ), - - # This renders the summary datatable at the bottom of the interface once data is imported - fluidRow( - dataTableOutput("channels") + + # Utilizing javascript within Shiny allows for added features such as enable/disable of inputs + useShinyjs(), + + # Shinyalert is a package that makes interactive"pop-ups" (modals) easy to generate + useShinyalert(), + setBackgroundColor( + color = c("ghostwhite", "lightgrey"), + gradient = "linear", + direction = "bottom", + shinydashboard = FALSE + ), + fluidRow(headerPanel("Electrochemical Data Processing Tool (EcDPT)")), + fluidRow("Analysis Mode", + switchInput("batchProcessing", onLabel = "Batch Processing", offLabel = "Single Analysis", handleWidth = "200px"), + align = "center", style = "margin: 0%;" + ), + + # This first column is where most user inputs are, with the exception of the directory name + column(4, + align = "center", + + # This generates the optional block in which the user can import a previous R environment + fluidRow( + strong("Start Here"), tags$br(), + "Current Cell Group: ", textOutput("currDir", inline = TRUE), tags$br(), + actionButton("chooseDir", "Cell Group Location*", class = "btn-secondary", style = "width: 80%; margin: 5%; font-size: 100%;"), tags$br(), + helpText("The analysis will create a folder within the selected folder."), + style = "border: 1px solid black; padding: 5%; margin: 5%;" + ), + fluidRow( + fileInput("rerun", "Optional: Import Previous R Environment", multiple = FALSE, accept = ".RData"), + actionButton("load", "Load"), + style = "border: 1px dashed black; margin: 5%; padding: 5%;" + ), + + # These are the"optional" parameters that need to be filled out if select graphs are selected + fluidRow( + strong("Optional Parameters"), tags$br(), + "Parameters responsible for certain graphs.", tags$br(), tags$br(), + + # Used for dishcharge areal capacity graphs + numericInput("area", "Limiting Electrode Area (cm^2)", 2.74, min = 0), + + # Used for C-Rate calculations + # numericInput("perActive","Active Loading of Limiting Electrode (wt%)", 96, min = 0, max = 100), + # numericInput("capActive","Capacity of Limiting Active Material (mAh/g)", 155, min = 0, max = 100), + style = "border: 1px dashed black; padding: 5%; margin: 5%;" + ), + ), + + # The second column is where selection of graphs and further features are selected + column(4, + align = "left", + fluidRow( + # Presents options for graphs to be generated + "Choose graphs to be generated: ", + actionButton("whatGraph", "What's this?", class = "btn-link"), + checkboxGroupInput("gGraphs", NULL, choices = c( + "Discharge Capacity", "Discharge Areal Capacity", + "Total Discharge Capacity", "Average Voltage", "Delta Voltage", "Capacity Loss" + ), inline = FALSE), + "Choose graphs to animate: ", + checkboxGroupInput("gAnim", NULL, choices = c("dQdV Plots", "Voltage Profiles"), inline = FALSE), + # "Advanced Analysis", + # radioButtons("advCalc", NULL, choices = c("No", "Yes"), inline = TRUE), + # helpText(HTML("Advanced Analysis includes:
  • C-Rate Calculations
  • Capacity Fade per Rate
  • Origin Export
")), + style = "margin: 5%; border: 1px solid black; padding: 5%;" + ), + ), + + # The final column is where all the"action" items are, aka clicking any of these buttons will trigger a process + column(4, + align = "center", + fluidRow( + selectInput("dirName", "Analysis Name*", c("Formation", "RateCap", "HighRate", "Constant Current", "CC-CV"), selected = "RateCap"), tags$br(), + strong("Files to be Analyzed*"), tags$br(), + "Import all Arbin files of interest.", tags$br(), tags$br(), + fileInput("files", NULL, multiple = TRUE), + actionButton("submit", "Begin Analysis", class = "btn-success", style = "width: 80%; height: 100px; margin: 5%; font-size: 100%;"), + actionButton("clearTable", "Clear Cells", class = "btn-danger", style = "width: 60%; margin: 5%; font-size: 100%;"), + style = "border: 4px double black; padding: 5%; margin: 5%;" + ), + + # This final block enables a button after data becomes available, which trigger the modal to build custom graphs + fluidRow( + strong("Custom Graph Builder"), tags$br(), + "Customize Graphs Once Data is Available", + disabled(actionButton("graphBuilder", "Launch", width = "80%", class = "btn-primary", style = "height: 50px; margin: 5%; font-size: 100%;")), tags$br(), + style = "border: 1px solid black; padding: 5%; margin: 5%" + ), + fluidRow( + strong("Optional: Import Active Material Masses from Excel"), tags$br(), + "Running Analysis without Masses Will Render Raw Capacities (Ah)", + textAreaInput("masses", NULL, height = "100px", resize = "vertical"), + helpText("Enter the masses separated by a new line and in the order they appear in the data table below."), + actionButton("excelImport", "Import"), + style = "border: 1px dashed black; padding: 5%; margin: 5%;" + ), + ), + + # This renders the summary datatable at the bottom of the interface once data is imported + fluidRow( + dataTableOutput("channels") + ) ) -) -} -) \ No newline at end of file +}) From 62d6738ff8e7c0a91c1ec255cc4b05ca2f7f52d5 Mon Sep 17 00:00:00 2001 From: rwaldheim <65979392+rwaldheim@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:57:14 -0400 Subject: [PATCH 18/19] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f40fbd8..45c9db7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ _site .jekyll-cache .jekyll-metadata vendor +.Rhistory From ca997965245bab85190bdd444e397c2ac684ffc8 Mon Sep 17 00:00:00 2001 From: rwaldheim <65979392+rwaldheim@users.noreply.github.com> Date: Fri, 10 Sep 2021 11:00:44 -0400 Subject: [PATCH 19/19] Delete .Rhistory --- .Rhistory | 512 ------------------------------------------------------ 1 file changed, 512 deletions(-) delete mode 100644 .Rhistory diff --git a/.Rhistory b/.Rhistory deleted file mode 100644 index 7bd7632..0000000 --- a/.Rhistory +++ /dev/null @@ -1,512 +0,0 @@ -devtools::install_github("eddelbuettel/rbenchmark") -library('devtools') -install.packages('devtools') -devtools::install_github("eddelbuettel/rbenchmark") -library('devtools') -install_github("eddelbuettel/rbenchmark") -install.packages("devtools") -install_github("eddelbuettel/rbenchmark") -install.packages("C:/Users/rwaldhei/Downloads/rbenchmark_1.0.0.tar.gz", repos = NULL, type = "source") -library('rbenchmark') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -install.packages("rlang") -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library('rbenchmark') -install.packages("rlang") -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -sessionIfo() -sessionInfo() -library('rlang') -sessionInfo() -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -install.packages("rlang", version="0.4.6") -library('rbenchmark') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -install.packages("tidyverse") -install.packages("tidyverse") -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library('rbenchmark') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -install.packages("C:/Users/rwaldhei/Downloads/rlang_0.4.10.tar.gz", repos = NULL, type = "source") -uninstall.package('rlang') -uninstall.packages('rlang') -remove.packages('rlang') -install.packages("C:/Users/rwaldhei/Downloads/rlang_0.4.10.tar.gz", repos = NULL, type = "source") -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -install.packages("installr") -library(installr) -updateR() -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library('shinyjs') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -list.dirs() -list.dirs(path = ".", full.names = TRUE, recursive = TRUE) -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -base_names -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -data$data -data -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -data -data$data$sheet -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -data -raw_data -raw_data$sheet -data$sheet -data <<- data[raw_data$sheet %in% data$sheet] -data -data[raw_data$sheet %in% data$sheet] -data[intersect(raw_data$sheet, data$sheet)] -intersect(raw_data$sheet, data$sheet) -new_rows <- intersect(raw_data$sheet, data$sheet) -data[data$sheet %in% new_rows] -data%sheet %in% new_rows -data$sheet %in% new_rows -data[which(data$sheet %in% new_rows),] -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -profvis::profvis({ # Modal for completed analysis -shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation(), "/", input$dirName, sep = ""), -type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", -callbackR = function(x) { -if (x) { -export_to_origin() -} -} -)}) -profvis::profvis({ # Sets up a progress bar in which to estimate how long the execution of the code will take -progress <- Progress$new(session, min = 0, max = nrow(data)) -progress$set(message ="Plugging and chugging...\n", detail ="Starting up...") -# Closes all graphics devices that may be lingering (prevents an excess from opening and slowing down the analysis) -while (dev.cur() != 1) { -dev.off() -} -# Resets the variables for the graph builder so new results are concatenated to old ones -numCycles <<- data.frame() -dQdVData <<- data.frame() -total <<- data.frame() -cycle_facts <<- data.frame() -# Disable all input fields to prevent errors occurring from changing values -disable("files") -disable("lowV") -disable("highV") -disable("dirLocation()") -disable("submit") -disable("excelImport") -disable("gGraphs") -disable("peakFit") -disable("area") -disable("perActive") -disable("capActive") -# Creates the directory in which all data will be stored -dir.create(paste(dirLocation(), input$dirName, sep = "/")) -# Update the status once all set-up functions are complete -progress$set(detail ="Starting first cell...") -# ###### -# -# The bulk of the analysis occurs within the loop. Each iteratin of the loop corresponds to a cell. -# -# ###### -for (row in 1:nrow(data)) { -# ###### -# -# This is where all code that should be executed on a"per cell" basis, to prepare for analysis -# -# ###### -# Import the excel sheet corresponding to cell of interest -tmp_excel <- read_excel(toString(data$datapath[row]), toString(data$sheet[row])) -# Create an nested directory for all the data and, if applicable, then further folders for graphs of interest -dir.create(paste(dirLocation(), "/", input$dirName, data$sheet[row], sep ="/")) -if (is.element("dQdV Graphs", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"dQdV Plots", sep ="/")) -if (is.element("Voltage Profiles", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage Profiles", sep ="/")) -if (is.element("Voltage vs. Time", input$gGraphs)) dir.create(paste(dirLocation(), input$dirName, data$sheet[row],"Voltage v Time", sep ="/")) -# Check if masses have been imported, if they have not then all future calculations will be done on a raw capacity basis -if (sum(data$Mass) != 0) { -ylabel <-"Capacity (mAh/g)" -tmp_excel$Q.d <- as.numeric(tmp_excel$`Discharge_Capacity(Ah)` * (1000 / data$Mass[row])) -tmp_excel$Q.c <- as.numeric(tmp_excel$`Charge_Capacity(Ah)`* (1000 / data$Mass[row])) -tmp_excel$CC <- tmp_excel$Q.d - tmp_excel$Q.c -tmp_excel$CE <- (tmp_excel$Q.d / tmp_excel$Q.c) * 100 -} else { -ylabel <-"Capacity (Ah)" -tmp_excel$CC <- tmp_excel$`Discharge_Capacity(Ah)` - tmp_excel$`Charge_Capacity(Ah)` -tmp_excel$CE <- (tmp_excel$`Discharge_Capacity(Ah)` / tmp_excel$`Charge_Capacity(Ah)`) * 100 -} -tmp_excel$Cell <- row -tmp_excel$CE[is.infinite(tmp_excel$CE)|is.nan(tmp_excel$CE)|tmp_excel$CE > 200] <- 0; -# ###### -# -# This loop iterates through each cycle of the cell. -# -# ###### -cycles <- split(tmp_excel, tmp_excel$Cycle_Index) -prev_c <- 0 -lastCC <- 0 -ch_dch <- TRUE -durations <- vector(length = 4) -caps <- vector(length = 4) -prev <- TRUE -dchV <- 0 -chV <- 0 -i <- 1 -for (cycle in cycles) { -# ###### -# -# Within each cycle, take out the individual steps. These include the charge, discharge, and others. -# -# ###### -progress$set(detail = paste("Analyzing cell", row,", cycle", i)) -steps <- split(cycle, cycle$Step_Index) -n <- 1 -for (step in steps) { -# ###### -# -# Isolation of the charge and discharge cycles. The algorithm is as follows: -# -# If the change in voltage for the step is greater than 0.5V, it is a charge or dicharge cycle. Then, the direction (sign) of the current determines -# if it is charge or discharge (positive current = charge step, negative current is a discharge cycle). -# -# ###### -if (abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) > 0.5) { -# ###### -# -# All code that should be executed for every charge/discharge cycles should be written here. -# -# ###### -lastCC <- n -if (step$'Current(A)'[[1]] > 0) { -chV <- (1 / (tail(step$`Charge_Capacity(Ah)`,1) - step$`Charge_Capacity(Ah)`[[1]])) * trapz(step$`Charge_Capacity(Ah)`, step$`Voltage(V)`) -dQCdV <- diff(step$`Charge_Capacity(Ah)`)/diff(step$`Voltage(V)`) -dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQCdV)+1), cell = rep(row, length(dQCdV)+1), c_d=rep(0, length(dQCdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQCdV), F_L=rep(0,length(dQCdV)+1))) -durations[1] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] -caps[1] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] -ch_dch <- TRUE -} else { -dchV <- (1 / (tail(step$`Discharge_Capacity(Ah)`,1) - step$`Discharge_Capacity(Ah)`[[1]])) * trapz(step$`Discharge_Capacity(Ah)`, step$`Voltage(V)`) -dQDdV <- diff(step$`Discharge_Capacity(Ah)`)/diff(step$`Voltage(V)`) -durations[3] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] -caps[3] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] -ch_dch <- FALSE -if (abs(prev_c - step$`Current(A)`[[1]]) > 0.0005) { -dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(1,length(dQDdV)+1))) -prev_c = step$`Current(A)`[[1]] -} else { -dQdVData <<- rbind(dQdVData, data.frame(cycle=rep(i, length(dQDdV)+1), cell = rep(row, length(dQDdV)+1), c_d=rep(1, length(dQDdV)+1), voltage=step$`Voltage(V)`, dQdV=c(0, dQDdV), F_L=rep(0, length(dQDdV)+1))) -} -} -} else if (n - lastCC == 1 & abs(tail(step$'Voltage(V)',1) - step$'Voltage(V)'[[1]]) < 0.001) { -if (step$'Current(A)'[[1]] > 0) { -durations[2] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] -caps[2] <- tail(step$'Charge_Capacity(Ah)', 1) - step$'Charge_Capacity(Ah)'[[1]] -} else { -durations[4] <- tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] -caps[4] <- tail(step$'Discharge_Capacity(Ah)', 1) - step$'Discharge_Capacity(Ah)'[[1]] -} -} -ch_dch <- FALSE -n <- n + 1 -} -# ###### -# -# Code meant to be run on data"per cycle" should be written here -# -# ###### -dQdVData <<- dQdVData[is.finite(dQdVData$voltage),] -dQdVData <<- dQdVData[is.finite(dQdVData$dQdV),] -if (sum(data$Mass) != 0) { -DCap <- tail(cycle$Q.d, 1) -CCap <- tail(cycle$Q.c, 1) -} else { -DCap <- tail(cycle$`Discharge_Capacity(Ah)`, 1) -CCap <- tail(cycle$`Charge_Capacity(Ah)`, 1) -} -timeCVFracCh <- durations[1] / (durations[1] + durations[2]) -timeCVFracDch <- durations[3] / (durations[3] + durations[4]) -capCVFracCh <- caps[1] / (caps[1] + caps[2]) -capCVFracDch <- caps[3] / (caps[3] + caps[4]) -# Record charge and discharge voltage, then calculate the delta and average voltage -cycle_facts <<- rbind(cycle_facts, data.frame(cycle=i, cell=row, chV=chV, dchV=dchV, avgV=(dchV + chV) / 2, -dV=chV-dchV, DCap = DCap, CCap = CCap, CE = (CCap / DCap) * 100, lostCap = CCap - DCap, cellFade = if (i == 1) 0 else {DCap - tail(cycle_facts$DCap, 1)}, -cycleTime = tail(cycle$`Test_Time(s)`, 1) - cycle$`Test_Time(s)`[[1]], timeCVFracCh = timeCVFracCh, timeCVFracDch = timeCVFracDch, -capCVFracCh = capCVFracCh, capCVRatioDch = capCVFracDch)) -i <- i + 1 -} -# ###### -# -# Code meant to be run on data"per cell" should be written here -# -# ###### -cell_data <- cycle_facts[cycle_facts$cell == row,] -# Discharge capacity plotting, with coulombic efficiency being plotted alongside -if (is.element("Discharge Capacity", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Capacity Plot.png", sep ="")) -eol <- cell_data$`DCap`[[1]] * 0.8 -plot(cell_data$cycle, cell_data$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) -abline(h=eol, lty ="dotted") -par(new = T) -plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) -mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") -axis(side = 4, col ="red", col.axis = "red") -dev.off() -} -# Discharge areal capacity plotting, with coulombic efficiency being plotted alongside -if (is.element("Discharge Areal Capacity", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Discharge Areal Capacity Plot.png", sep ="")) -new_par <- old_par <- par("mar") -new_par[4] <- old_par[2] -par(mar = new_par) -eol <- ((cell_data$DCap[[1]] * 1000) / data$area[row]) * 0.8 -plot(cell_data$cycle, ((cell_data$DCap * 1000) / data$area[row]), type ="p", main=paste("Discharge Areal Capacity for", input$dirName), xlab=NA, ylab="Discharge Capacity (mAh/cm^2)", mai = c(1,1,1,2)) -abline(h=eol, lty ="dotted") -par(new = T) -plot(cell_data$cycle, cell_data$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) -mtext(side = 4, line = 3,"Coulombic Efficiency (%)", col = "red") -axis(side = 4, col ="red", col.axis = "red") -dev.off() -} -# Average voltage plotting -if (is.element("Average Voltage", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Average Voltage Plot.png", sep ="")) -plot(cell_data$cycle, cell_data$chV, col="blue", main=paste("Average Voltage Plot for", input$dirname, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim=c(min(cell_data[,2:4]), max(cell_data[,2:4]))) -points(cell_data$cycle, cell_data$dchV, col="red", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") -points(cell_data$cycle, cell_data$avgV, col="black", main=paste("Average Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)") -legend("bottomright", c("Charge Voltage","Discharge Voltage","Average Voltage"), col=c("blue","red","black"), pch=19) -dev.off() -} -# Delta voltage plotting -if (is.element("Delta Voltage", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Delta Voltage Plot.png", sep ="")) -plot(cell_data$cycle, cell_data$dV, main=paste("Delta Voltage Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab="Voltage (V)", ylim =c(0, 0.5)) -dev.off() -} -# Capacity Loss plotting -if (is.element("Capacity Loss", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," Capacity Loss Plot.png", sep ="")) -plot(cell_data$cycle, cell_data$lostCap, main=paste("Capacity Loss Plot for", input$dirName, data$sheet[row]), xlab="Cycle", ylab= ylabel, ylim = c(mean(cell_data$lostCap) + (2* sd(cell_data$lostCap)), mean(cell_data$lostCap) - (1.5* sd(cell_data$lostCap)))) -abline(h=median(cell_data$lostCap), lty="dotted") -dev.off() -} -if (is.element("dQdV Plots", input$gAnim)) { -dQdVplot <- function(){ -tmp_data <- dQdVData[dQdVData$cell == row,] -first_cycle <- dQdVData[dQdVData$cell == row & dQdVData$cycle == 2,] -datalist <- split(tmp_data, tmp_data$cycle) -lapply(datalist, function(plotData){ -p <- plot(plotData$voltage, plotData$dQdV, main=paste("dQdV Plot for", input$dirName, data$sheet[row], "Cycle", plotData$cycle[[1]]), xlab="Voltage (V)", ylab= "dQdV (Ah/V)", -xlim = c(min(tmp_data$voltage), max(tmp_data$voltage)), ylim = c(min(tmp_data$dQdV), max(tmp_data$dQdV))) + -points(first_cycle$voltage, first_cycle$dQdV, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) -}) -} -save_gif(dQdVplot(), paste(dirLocation(), input$dirName, data$sheet[row], "dQdV Animation.gif", sep = "/"), delay = 0.2) -} -if (is.element("Voltage Profiles", input$gAnim)) { -vpPlot <- function(){ -first_cycle <- tmp_excel[tmp_excel$`Cycle_Index` == 2,] -datalist <- split(tmp_excel, tmp_excel$`Cycle_Index`) -lapply(datalist, function(plotData){ -p <- plot(plotData$CC, plotData$`Voltage(V)`, main=paste("Voltage Profile for", input$dirName, data$sheet[row], "Cycle", plotData$`Cycle_Index`[[1]]), xlab=ylabel, ylab= "Voltage (V)", -xlim = c(min(tmp_excel$CC), max(tmp_excel$CC)), ylim = c(min(tmp_excel$`Voltage(V)`), max(tmp_excel$`Voltage(V)`))) + -points(first_cycle$CC, first_cycle$`Voltage(V)`, col = rgb(red = 1, green = 0, blue = 0, alpha = 0.5)) -}) -} -save_gif(vpPlot(), paste(dirLocation(), input$dirName, data$sheet[row], "Voltage Profile Animation.gif", sep = "/"), delay = 0.2) -} -# Save all data within the cell's directory -write.csv(tmp_excel, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row],".csv", sep ="")) -write.csv(dQdVData, file = paste(dirLocation(), "/", input$dirName,"/", data$sheet[row],"/", data$sheet[row]," dQdV Data.csv", sep ="")) -# Append summation data to the larger datasets to be worked with later -final <- rbind(final, tmp_excel) -numCycles <<- rbind(numCycles, data.frame(sheet=data$sheet[row], cycles=nrow(cell_data))) -# Update progress bar -progress$set(value = row, detail = paste("Finished", row," of", nrow(data)," cells.")) -} -# ###### -# -# Code meant to be run on all data of all cells should be written here. -# -# ###### -# With iterations complete, final calculations are being worked -progress$set(detail ="Wrapping up...") -# Get the last status of each cycle for each cell (namely capacity) -DCap <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(mean) -CE <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(mean) -capSEs <- cycle_facts[c("cycle","DCap")] %>% group_by(cycle) %>% summarise_each(se) -ceSEs <- cycle_facts[c("cycle","CE")] %>% group_by(cycle) %>% summarise_each(se) -stats <- data.frame(cbind(cycle = capSEs$cycle, DCap = DCap$DCap, CE = CE$CE, capSE = capSEs$DCap, ceSE = ceSEs$CE)) -# Send all the data to a global variable to be used elsewhere -total <<- final -tryCatch({ -# Total dishcharge capacity plotting -if (is.element("Total Discharge Capacity", input$gGraphs)) { -png(paste(dirLocation(), "/", input$dirName,"/", "Total Discharge Capacity Plot.png", sep ="")) -eol <- max(stats$DCap) * 0.8 -plot(stats$cycle, stats$DCap, type ="p", main=paste("Discharge Capacity for", input$dirName), xlab=NA, ylab=paste("Discharge", ylabel), mai=c(1,1,1,1)) -arrows(stats$cycle, stats$DCap - stats$capSE, stats$cycle, stats$DCap + stats$capSE, length=0.05, angle=90, code=3) -abline(h=eol, lty ="dotted") -par(new = T) -plot(stats$cycle, stats$CE, type ="p", axes=F, col ="red", ylab=NA, xlab="Cycle", ylim = c(0, 105)) -arrows(stats$cycle, stats$CE - stats$ceSE, stats$cycle, stats$CE + stats$ceSE, length=0.05, angle=90, code=3, col ="red") -axis(side = 4, col ="red") -mtext(side = 4, line = 2,"Coulombic Efficiency (%)") -dev.off() -} -}, error = function(cond) { -print(cond) -}) -# Save total data and stats -write.csv(stats, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Summary.csv", sep ="")) -write.csv(cycle_facts, file = paste(dirLocation(), "/", input$dirName,"/", basename(dirLocation())," Cycle Facts.csv", sep ="")) -# If a histor directory does not exist, create it. Save all the data revelant to plotting to a RData file. -if (!dir.exists(paste(dirLocation(), "history", sep = "/"))) { -dir.create(paste(dirLocation(), "history", sep = "/")) -} -dirName <<- input$dirName -save(dirLocation, dirName, data, dQdVData, total, cycle_facts, numCycles, file = paste(dirLocation(), "/history/", input$dirName, ".RData", sep = "")) -# Modal for completed analysis -shinyalert("Analysis Complete!", paste("All your data are now in ", dirLocation(), "/", input$dirName, sep = ""), -type ="success", showConfirmButton = TRUE, showCancelButton = TRUE, confirmButtonText = "Generate Origin File", cancelButtonText = "Continue", -callbackR = function(x) { -if (x) { -export_to_origin() -} -} -) -# Finish progress bar -progress$set(value = nrow(data)) -# Re-enable all input fields, including graph builder -enable("files") -enable("lowV") -enable("highV") -enable("dirLocation()") -enable("submit") -enable("excelImport") -enable("gGraphs") -enable("peakFit") -enable("area") -enable("perActive") -enable("capActive") -enable("graphBuilder") -# Close progress bar -progress$close()}) -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -view(data) -View(raw_data) -View(data)_ -View(data) -View(data) -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -View(tmp_excel) -View(tmp_excel) -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -View(step) -tail(step$'Test_Time(s)', 1) -step$'Test_Time(s)'[[1]] -tail(step$'Test_Time(s)', 1) - step$'Test_Time(s)'[[1]] -install.packages("lubridate") -library(lubridate) -period_to_seconds(tail(step$'Test_Time(s)', 1)) - period_to_seconds(step$'Test_Time(s)'[[1]]) -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -library(shiny); runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -runApp('C:/Users/rwaldhei/Documents/ecdpt/arbinimport.R') -devtools::uninstall('echemAnalyst') -setwd("C:/Users/rwaldhei/Documents/ecdpt") -devtools::create('echemAnalyst') -library(shiny); runApp('arbinimport.R')