# CHNOSZ/demo/ORP.R 
# First version 20100715 jmd
library(CHNOSZ)

# Calculate the temperature dependence of 
# potentials vs. standard hydrogen electrode (SHE) of various electrodes (Ag/AgCl)
# and ORP standards (ZoBell, Light's, (tri)iodide) 

# Use the extended Debye-Huckel equation with Alberty's parameters
oldnon <- nonideal("Alberty")

# Bard et al.'s fit to the potential
# (Bard, Parson, Jordan, Standard Potentials In Aqueous Solution, 1985)
AgAgCl.Bard <- function(T, high.T = TRUE) {
  # we use the corrected high-T formula from wikipedia
  if(high.T) return(0.23737 - 5.3783e-4 * T - 2.3728e-6 * T^2 - 2.2671e-9 * (T+273))
  else return(0.23695 - 4.8564e-4 * T - 3.4205e-6 * T^2 - 5.869e-9 * (T+273))
}

# Function to calculate the potential of Ag/AgCl vs. SHE
# Ag(s) + Cl- = AgCl(s) + e-
# logK = -pe - logaCl
# pe = -logK - logmCl - loggamCl
# ORP = RT/F * (logK - logmCl - loggamCl)
AgAgCl <- function(T, mKCl = 4) {
  # mKCl is the molality of KCl in the electrolyte
  # We take it as a first approximation to be equal to
  # the molality of Cl- (and to the ionic strength)
  logmCl <- log10(mKCl)
  # Get the logK for the reaction
  logK <- subcrt(c("Ag", "Cl-", "AgCl", "e-"), c(-1, -1, 1, 1), c("cr", "aq", "cr", "aq"), T = T)$out$logK
  # Get the activity coefficient for Cl-
  loggamCl <- log(10) * subcrt("Cl-", T = T, IS = mKCl)$out[[1]]$loggam
  # Get the pe for the solution
  pe <- -logK - logmCl - loggamCl
  # Convert that to Eh
  Eh <- convert(pe, "Eh", T = convert(T, "K"))
  return(Eh)
}

ZoBell <- function(T) {
  # Doesn't work very well because we ignore the
  # ferricyanide and ferrocyanide complexes
  # Fe+2 = Fe+3 + e-
  # logK = logaFe3 - logaFe2 - pe
  # Get the logK for the reaction
  logK <- subcrt(c("Fe+2", "Fe+3", "e-"), c(-1, 1, 1), T = T)$out$logK
  # We use the recipe from standard methods (table 2580:II)
  # 1.4080 g K4Fe(CN)6.3H2O -> 0.0033333 mol Fe+2
  # 1.0975 g K3Fe(CN)6      -> 0.0033333 mol Fe+3
  # 7.4555 g KCl            -> 0.1 mol Cl-
  logmFe2 <- logmFe3 <- log10(0.0033333)
  # Get the loggam for the iron species
  loggamFe2 <- log(10) * subcrt("Fe+2", T = T, IS = 1)$out[[1]]$loggam
  loggamFe3 <- log(10) * subcrt("Fe+3", T = T, IS = 1)$out[[1]]$loggam
  # Get the pe for the solution
  pe <- -logK + logmFe3 + loggamFe3 - logmFe2 - loggamFe2
  # Convert to Eh
  Eh <- convert(pe, "Eh", T = convert(T, "K"))
  return(Eh)
}

ZoBell.table <- function(T = NULL, which = NULL) {
  # Oxidation-reduction potential of ZoBell's solution 
  # from Standard Methods for Water and Wastewater or YSI 
  # (interpolated and/or extrapolated as necessary)
  # Standard methods (1997) table 2580:I
  Eh.T.SMW <- 1:30
  Eh.SMW <- c(0.481, 0.479, 0.476, 0.474, 0.472, 0.47, 0.468, 0.465, 0.463, 0.461, 
  0.459, 0.457, 0.454, 0.452, 0.45, 0.448, 0.446, 0.443, 0.441, 0.439, 0.437, 
  0.435, 0.432, 0.43, 0.428, 0.426, 0.424, 0.421, 0.419, 0.417)
  # From YSI (2005):
  # Measuring ORP on YSI 6-Series Sondes: Tips, Cautions and Limitations
  # NOTE: these values are vs. Ag/AgCl (4 M KCl)
  Eh.T.YSI <- seq(-5, 50, by = 5)
  Eh.YSI <- c(267.0, 260.5, 254.0, 247.5, 241.0, 234.5, 228.0, 221.5, 215.0, 208.5, 202.0, 195.5)/1000
  # Spline function for each of the tables
  SMW <- splinefun(Eh.T.SMW, Eh.SMW)
  YSI <- splinefun(Eh.T.YSI, Eh.YSI)
  # Just one of the tables
  Eh.fun <- get(which)
  Eh.T <- get(paste("Eh.T", which, sep = "."))
  if(is.null(T)) T <- Eh.T
  return(data.frame(T = T, Eh = Eh.fun(T)))
}

Light <- function(T) {
  # This is going to look something like
  # Fe+2 = Fe+3 + e-
  # logK = logaFe3 - logaFe2 - pe
  # Get the logK for the reaction
  logK <- subcrt(c("Fe+2", "Fe+3", "e-"), c(-1, 1, 1), T = T)$out$logK
  # We use the recipe from standard methods (table 2580:II)
  # 39.21 g Fe(NH4)2(SO4)2(H2O)6 -> 0.1 mol Fe+2
  # 48.22 g Fe(NH4)(SO4)2(H2O)12 -> 0.1 mol Fe+3
  logmFe2 <- logmFe3 <- log10(0.1)
  # Get the loggam for the iron species
  loggamFe2 <- log(10) * subcrt("Fe+2", T = T, IS = 0.2)$out[[1]]$loggam
  loggamFe3 <- log(10) * subcrt("Fe+3", T = T, IS = 0.2)$out[[1]]$loggam
  # Get the pe for the solution
  pe <- -logK + logmFe3 + loggamFe3 - logmFe2 - loggamFe2
  # Convert to Eh
  Eh <- convert(pe, "Eh", T = convert(T, "K"))
  return(Eh)
}

Iodide.table <- function(T=NULL) {
  # Oxidation-reduction potential of Thermo's iodide solution
  # from thermo instruction sheet 255218-001 (articlesFile_18739)
  T.Iodide <- seq(0, 50, 5)
  Eh.Iodide <- c(438, 435, 431, 428, 424, 420, 415, 411, 406, 401, 396)/1000
  Iodide <- splinefun(T.Iodide, Eh.Iodide)
  if(is.null(T)) T <- T.Iodide
  return(data.frame(T = T, Eh = Iodide(T)))
}

Iodide <- function(T) {
  # This is going to look something like
  # 3I- = I3- + 2e-
  # logK = -2pe + logaI3 - 3logaI
  # Get the logK for the reaction
  logK <- subcrt(c("I-", "I3-", "e-"), c(-3, 1, 2), T = T)$out$logK
  # Could the activities be 0.1 M ... or something else?
  logmI <- log10(2)
  logmI3 <- log10(0.01)
  # Get the loggam for the iodine species
  loggamI <- log(10) * subcrt("I-", T = T, IS = 0.2)$out[[1]]$loggam
  loggamI3 <- log(10) * subcrt("I3-", T = T, IS = 0.2)$out[[1]]$loggam
  # Get the pe for the solution
  pe <- ( -logK + logmI3 + loggamI3 - 3 * (logmI - loggamI) ) / 2
  # Convert to Eh
  Eh <- convert(pe, "Eh", T = convert(T, "K"))
  return(Eh)
}

figure <- function() {
  # Make some figures
  # Temperatures in degrees C
  T <- seq(0, 100, 5)
  # Temperature-Eh diagram for various electrodes
  thermo.plot.new(ylim = c(0, 0.8), xlim = c(0, 100), 
    ylab = axis.label("Eh"), xlab = axis.label("T"))
  # Ag/AgCl electrode (Bard et al. fit)
  points(T, AgAgCl.Bard(T), pch = 0)
  # Ag/AgCl electrode (equilibrium calculations) 
  lines(T, AgAgCl(T))
  # ZoBell's solution (SMW table 2580)
  SMW <- ZoBell.table(which = "SMW")
  points(SMW$T, SMW$Eh, pch = 1)
  # ZoBell's solution (YSI tech report table)
  YSI <- ZoBell.table(which = "YSI")
  # Make these values referenced to SHE instead of Ag/AgCl
  Eh.YSI <- YSI$Eh + AgAgCl(YSI$T)
  points(YSI$T, Eh.YSI, pch = 2)
  # Light's solution (equilibrium values)
  lines(T, Light(T))
  # Light's solution (at 25 degrees only)
  points(25, 0.475 + 0.200, pch = 3)
  # Thermo's I-/I3- solution
  Thermo <- Iodide.table()
  points(Thermo$T, Thermo$Eh, pch = 4)
  # Calculated I-/I3- values
  lines(T, Iodide(T))
  # Add some labels
  text(c(30, 30, 30, 50), c(0.72, 0.5, 0.35, 0.25), 
    c("Light", "ZoBell", "(Tri)Iodide", "Ag/AgCl"))
  title(main = "Potentials vs standard hydrogen electrode (SHE)")
}

# Finally, make the plot
figure()

# Reset the nonideality method to default
nonideal(oldnon)
