library(knitr)
library(kableExtra)
knitr::opts_knit$set(root.dir = '/Users/scottsaunders/OneDrive - California Institute of Technology/Documents/Summer 2018/IDA_biofilms/09_11_18_DapEstimation')
knitr::opts_chunk$set(tidy.opts=list(width.cutoff=60),tidy=TRUE)
knitr::opts_chunk$set(echo = TRUE, message=F,warning=F,fig.align="center")
suppressMessages(library('tidyverse'))
suppressMessages(library('cowplot'))
suppressMessages(library('broom'))
suppressMessages(library(modelr))
suppressMessages(library(viridis))
theme_set(theme_bw())
The following analysis is based on data acquired during the 09/04/18 IDA biofilm experiment. During this experiment I grew two dPHZ* biofilms and exposed them to PYO. I let the biofilms equilibrate during a transfer step after each soak, as usual. For this experiment I also tried to collect usable potential step scans. The scans were 30 seconds with three steps. First the baseline step was at -170mV, then after 10 seconds we jumped to -300mV and after another 10 seconds it jumped back to -170mV. Everything that follows is only from the first reductive step (from -170 to -300) which should be reducing PYO.
Also note that IDA A and B echem was done using separate potentiostats. IDA A was collected on the CHI 760b and IDA B was collected on the CHI 760e. Apparently the newer 760e can collect data points slightly faster (~0.2ms vs. 0.8ms). I totally messed up IDA B by accidentally unclipping the reference during a run, but I wanted to compare the data to see if the collection rate had a big influence.
files1 <- paste("idaB", dir(path = "/Users/scottsaunders/OneDrive - California Institute of Technology/Documents/Summer 2018/IDA_biofilms/09_11_18_DapEstimation/idaB",
pattern = "*.txt"), sep = "/")
# reads each file (from above) into a dataframe that is
# stored in another data frame with the filename
df1 <- data_frame(filename = files1) %>% mutate(file_contents = map(filename,
~read_csv(., col_names = c("a", "b", "c", "d"))))
potStepIdaB <- unnest(df1) %>% separate(filename, c("subDir",
"filename"), sep = "/") %>% separate(filename, c("strain",
"IDA", "reactor", "PHZadded", "molecule", "condition", "echem",
"rep"), sep = "_") %>% mutate(PHZaddedInt = as.integer(str_extract(PHZadded,
"^[0-9]+"))) %>% mutate(reactor = "M", t = a, current = b) %>%
select(-subDir, -a, -b, -c, -d) %>% filter(echem == "potStep")
files2 <- paste("raw090418potStepIDAa", dir(path = "/Users/scottsaunders/OneDrive - California Institute of Technology/Documents/Summer 2018/IDA_biofilms/09_11_18_DapEstimation/raw090418potStepIDAa",
pattern = "*.txt"), sep = "/")
# reads each file (from above) into a dataframe that is
# stored in another data frame with the filename
df2 <- data_frame(filename = files2) %>% mutate(file_contents = map(filename,
~read_csv(., col_names = c("t", "current"))))
potStepCsigFigs <- unnest(df2) %>% separate(filename, c("subDir",
"filename"), sep = "/") %>% separate(filename, c("strain",
"IDA", "reactor", "PHZadded", "molecule", "condition", "echem",
"rep"), sep = "_") %>% mutate(PHZaddedInt = as.integer(str_extract(PHZadded,
"^[0-9]+"))) %>% mutate(reactor = "C") %>% select(-subDir)
# must export with enough sig figs!!!!???
## make subsets for the first 300ms of the reduction steps
idaAsubsetSoak <- potStepCsigFigs %>% filter(condition == "soak" &
rep == "s5.txt")
idaAsubsetTran <- potStepCsigFigs %>% filter(condition == "tran" &
rep == "s7.txt")
idaAsubset <- bind_rows(idaAsubsetSoak, idaAsubsetTran) %>% filter(t >
10 & t <= 10.5) %>% mutate(t = t - 10) %>% mutate(t0.5 = t^-0.5)
idaBsubsetSoak <- potStepIdaB %>% filter(condition == "soak" &
rep == "s5.txt")
idaBsubsetTran <- potStepIdaB %>% filter(condition == "tran" &
rep == "s7.txt")
idaBsubset <- bind_rows(idaBsubsetSoak, idaBsubsetTran) %>% filter(t >
10 & t <= 10.5) %>% mutate(t = t - 10) %>% mutate(t0.5 = t^-0.5)
ggplot(idaAsubset %>% filter(t >= 0.002), aes(x = t, y = current,
color = PHZaddedInt)) + geom_point() + scale_color_viridis() +
facet_wrap(~condition, scales = "free") + labs(title = "IDA A",
y = "Current (A)", x = "time (sec)")
Examining the raw data shows that the current decay does in fact seem sensitive to the concentration of PYO that the electrode was soaked in. You can see the challenge of quantifying the transfer step since the noise is quite large compared to the signal.
ggplot(idaBsubset %>% filter(t >= 0.002), aes(x = t, y = current,
color = PHZaddedInt)) + geom_point() + scale_color_viridis() +
facet_wrap(~condition, scales = "free") + labs(title = "IDA B",
y = "Current (A)", x = "time (sec)")
The IDA B data is kinda messed up (not sure why 200uM is negative - maybe I changed the params?), but the soak steps look reasonably similar to IDA A.
Integration should yield the total amount of charge passed through the electrode over the time frame. In theory, converting charge to mol PYO should give the concentration of PYO at the electrode (if we know the relevant volume).
Integration is performed using trapezoid method.
idaAint <- idaAsubset %>% filter(t >= 0.002 & t < 0.3) %>% arrange(t) %>%
group_by(PHZadded, PHZaddedInt, rep, condition) %>% mutate(int = (lead(t) -
t) * (lead(abs(current)) + abs(current))/2) %>% mutate(cumInt = cumsum(int))
idaBint <- idaBsubset %>% filter(t > 0.002 & t < 0.3) %>% arrange(t) %>%
group_by(PHZadded, PHZaddedInt, rep, condition) %>% mutate(int = (lead(t) -
t) * (lead(abs(current)) + abs(current))/2) %>% mutate(cumInt = cumsum(int))
ggplot(idaAint, aes(x = t, y = cumInt, color = PHZaddedInt)) +
geom_point() + scale_color_viridis() + facet_wrap(~condition,
scales = "free") + labs(x = "time (sec)", y = "Charge (Coulombs)",
title = "Accumulated Charge over time")
Accumulated Charge again seems to scale pretty well with added PYO.
ggplot(idaBint, aes(x = t, y = cumInt, color = PHZaddedInt)) +
geom_point() + scale_color_viridis() + facet_wrap(~condition,
scales = "free") + labs(x = "time (sec)", y = "Charge (Coulombs)",
title = "Accumulated Charge over time")
F=96485.3329
A=0.039 #cm^2
z=0.001 #cm (10um)
idaAintSumm <- idaAint %>%
group_by(PHZadded,PHZaddedInt,rep,condition) %>%
summarise(sumInt=sum(int,na.rm=T)) %>%
mutate(PYOmol=sumInt/(2*F)) %>% #convert integration (coulombs) to moles PYO
mutate(PYOconcMperCm3=PYOmol/(A*z)) %>%
mutate(PYOconcMperL=PYOconcMperCm3*1000)
idaBintSumm <- idaBint %>%
group_by(PHZadded,PHZaddedInt,rep,condition) %>%
summarise(sumInt=sum(int,na.rm=T)) %>%
mutate(PYOmol=sumInt/(2*F)) %>% #convert integration (coulombs) to moles PYO
mutate(PYOconcMperCm3=PYOmol/(A*z)) %>%
mutate(PYOconcMperL=PYOconcMperCm3*1000)
\[C=\frac{Q}{nFAz} \]
\(F= 9.6485333\times 10^{4}\) Coulombs/mol (Faraday’s constant)
\(A= 0.039\) cm^2 (electrode area)
\(z= 10\) um (biofilm height/working volume)
So this calculated C value will be sensitive to amount of time we integrate (affects Q) and our value of z. For the soak step, it seems we may have actually chosen good values, because the calculated C is similar to the known amount of added PYO. Estimating C is useful in general, but will be very important for the Dap estimates. See further discussion below of what estimated C might mean…
ggplot(idaAintSumm, aes(x = PHZaddedInt, y = PYOconcMperL, color = condition)) +
geom_point() + geom_line() + facet_wrap(~condition, scales = "free") +
labs(title = "IDA A calculated [PYO] vs. added PYO", y = "Calculated [PYO] (M)",
x = "added [PYO] to soak (uM)")
This plot shows that accumulated charge (over this time scale) is a shockingly good proxy for concentration. The estimated concentration in Molar is very close to the amount of PYO (uM) added to the soak. Also the transfer step looks similar to the other electrochemical signals we measure…concentration seems to build up and then saturate. The question is, do we believe that the working volume of the electrode is the same in both cases? In the soak step the volume likely includes the biofilm and the solution, while in the transfer step only the biofilm has PYO in it…so this calculated concentration still isn’t quite how much is in the biofilm, but it’s how much is in the closest 10um to the electrode?
idaAechemPYO <- idaAintSumm %>% filter(condition == "tran") %>%
pull(PYOconcMperL)
tibble(transferStep = c(10, 25, 50, 75, 100, 150, 200), Measured_PYO_Solution = c(0.116,
0.13, 0.134, 0.188, 0.196, 0.342, 0.494), Calculated_PYO_Biofilm = idaAechemPYO *
10^6) %>% kable(digits = 12, format = "html", caption = "IDA A: Comparing PYO measured at electrode (echem) vs. PYO measured in Solution (LC-Ms) in uM") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)
transferStep | Measured_PYO_Solution | Calculated_PYO_Biofilm |
---|---|---|
10 | 0.116 | 4.514011 |
25 | 0.130 | 2.579644 |
50 | 0.134 | 5.954776 |
75 | 0.188 | 5.715927 |
100 | 0.196 | 2.731453 |
150 | 0.342 | 3.012939 |
200 | 0.494 | 3.904269 |
That said, if we believe the echem estimates for PYO concentration, they are significantly higher than what we measure in solution by LC-MS, so that’s encouraging. It suggests that the biofilms are still retaining PYO in the uM range at equilibrium.
ggplot(idaBintSumm, aes(x = PHZaddedInt, y = PYOconcMperL, color = condition)) +
geom_point() + geom_line() + facet_wrap(~condition, scales = "free") +
labs(title = "IDA B calculated [PYO] vs. added PYO", y = "Calculated [PYO] (M)",
x = "added [PYO] to soak (uM)")
idaBechemPYO <- idaBintSumm %>% filter(condition == "tran") %>%
pull(PYOconcMperL)
tibble(transferStep = c(5, 25, 200), Measured_PYO_Solution = c(0.114,
0.138, 0.488), Calculated_PYO_Biofilm = idaBechemPYO * 10^6) %>%
kable(digits = 12, format = "html", caption = "IDA B: Comparing PYO measured at electrode (echem) vs. PYO measured in Solution (LC-Ms) in uM") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)
transferStep | Measured_PYO_Solution | Calculated_PYO_Biofilm |
---|---|---|
5 | 0.114 | 4.665094 |
25 | 0.138 | 4.847746 |
200 | 0.488 | 2.406896 |
IDA A IDA B
5 0.16 0.114 10 0.116 0.12 25 0.13 0.138 50 0.134 0.136 75 0.188 0.124 100 0.196
150 0.342
200 0.494 0.488
From the Cottrell Equation: \[I(t)=\frac{nFAC \sqrt{D_{ap}}}{\sqrt{\pi t}}\]
\[I=\frac{nFAC \sqrt{D_{ap}}}{\sqrt{\pi}}t^{-1/2}\]
for I vs. t^-(1/2) \[slope=m=\frac{nFAC \sqrt{D_{ap}}}{\sqrt{\pi}}\]
\[D_{ap}= \pi (\frac{m}{nFAC})^2\]
or for I/A vs. T^-(1/2)
\[D_{ap}= \pi (\frac{m}{nFC})^2\]
This expression shows that the estimated Dap is extremely sensitive to the estimated C (scales with the square). Consequently, if we underestimate concentration by ~30% we would calculate a Dap 2x larger, and overestimating by 30% would decrease Dap by ~2x.
ggplot(idaAsubset %>% filter(condition == "soak" & rep == "s5.txt") %>%
filter(t >= 0.001), aes(x = t^-(0.5), y = current/A, color = PHZaddedInt)) +
geom_point() + scale_color_viridis() + geom_smooth(data = idaAsubset %>%
filter(condition == "soak" & rep == "s5.txt") %>% filter(t >=
0.01 & t <= 0.2), aes(x = t^-0.5, y = current/A, group = PHZaddedInt),
color = "black", method = "lm") + labs(title = "IDA A")
ggplot(idaBsubset %>% filter(condition == "soak" & rep == "s5.txt") %>%
filter(t >= 0.001), aes(x = t^-(0.5), y = current/A, color = PHZaddedInt)) +
geom_point() + scale_color_viridis() + geom_smooth(data = idaBsubset %>%
filter(condition == "soak" & rep == "s5.txt") %>% filter(t >=
0.01 & t <= 0.2), aes(x = t^-0.5, y = current/A, group = PHZaddedInt),
color = "black", method = "lm") + labs(title = "IDA A")
lmAsubset <- idaAsubset %>% filter(t >= 0.01 & t <= 0.2) %>%
group_by(PHZadded, PHZaddedInt, condition, rep) %>% do(tidy(lm(current/A ~
t0.5, .)))
lmBsubset <- idaBsubset %>% filter(t >= 0.01 & t <= 0.2) %>%
group_by(PHZadded, PHZaddedInt, condition, rep) %>% do(tidy(lm(current/A ~
t0.5, .)))
dapIdaA <- left_join(lmAsubset, idaAintSumm, by = c("PHZadded",
"PHZaddedInt", "condition", "rep")) %>% ungroup() %>% mutate(Dap = pi *
(estimate/(2 * F * PYOconcMperCm3))^2) %>% mutate(DapStdError = pi *
(std.error/(2 * F * PYOconcMperCm3))^2)
dapIdaB <- left_join(lmBsubset, idaBintSumm, by = c("PHZadded",
"PHZaddedInt", "condition", "rep")) %>% ungroup() %>% mutate(Dap = pi *
(estimate/(2 * F * PYOconcMperCm3))^2) %>% mutate(DapStdError = pi *
(std.error/(2 * F * PYOconcMperCm3))^2)
ggplot(dapIdaA %>% filter(term == "t0.5"), aes(x = factor(PHZaddedInt),
y = Dap, fill = condition)) + geom_col(position = "dodge",
color = "black") + geom_errorbar(aes(ymin = Dap - DapStdError,
ymax = Dap + DapStdError), position = "dodge") + labs(title = "Dap estimated from current decay (IDA A)",
x = "[PYO] soak (uM)")
Here the estimated Dap for the soak step is actually lower than the transfer step, which is not consistent with other measurements of Fernanda’s measurements. So the relationship between the soak and transfer steps did not match, but the estimates for Dap are somewhat close (within about an order of magnitude). See the table at the end.
ggplot(dapIdaB %>% filter(term == "t0.5"), aes(x = factor(PHZaddedInt),
y = Dap, fill = condition)) + geom_col(position = "dodge",
color = "black") + geom_errorbar(aes(ymin = Dap - DapStdError,
ymax = Dap + DapStdError), position = "dodge") + labs(title = "Dap estimated from current decay (IDA A)",
x = "[PYO] soak (uM)")
From the integrated Cottrell Equation: \[Q(t)=\frac{2nFAC \sqrt{D_{ap}t}}{\sqrt{\pi}}\]
\[Q(t)=\frac{2nFAC \sqrt{D_{ap}}}{\sqrt{\pi}}t^{-1/2}\]
for Q vs. t^(1/2) \[slope=m=\frac{2nFAC \sqrt{D_{ap}}}{\sqrt{\pi}}\]
\[D_{ap}= \pi (\frac{m}{2nFAC})^2\] The scaling of C is the same as chronoamperometry. Therefore this Dap is also sensitive to our C estimate.
Try to just fit the later linear portion of the plot (50ms to 250ms).
ggplot(idaAint %>% filter(t <= 0.25), aes(x = t^0.5, y = cumInt,
color = PHZaddedInt)) + geom_point() + scale_color_viridis() +
geom_smooth(data = idaAint %>% filter(t >= 0.05 & t <= 0.25),
method = "lm", color = "black", aes(x = t^0.5, y = cumInt,
group = PHZaddedInt)) + facet_wrap(~condition, scales = "free")
ggplot(idaBint %>% filter(t <= 0.25), aes(x = t^0.5, y = cumInt,
color = PHZaddedInt)) + geom_point() + scale_color_viridis() +
geom_smooth(data = idaBint %>% filter(t >= 0.05 & t <= 0.25),
method = "lm", color = "black", aes(x = t^0.5, y = cumInt,
group = PHZaddedInt)) + facet_wrap(~condition, scales = "free")
lmAsubsetQ <- idaAint %>% filter(t >= 0.05 & t <= 0.25) %>% group_by(PHZadded,
PHZaddedInt, condition, rep) %>% do(tidy(lm(cumInt ~ t0.5,
.)))
lmBsubsetQ <- idaBint %>% filter(t >= 0.05 & t <= 0.25) %>% group_by(PHZadded,
PHZaddedInt, condition, rep) %>% do(tidy(lm(cumInt ~ t0.5,
.)))
dapQidaA <- left_join(lmAsubsetQ, idaAintSumm, by = c("PHZadded",
"PHZaddedInt", "condition", "rep")) %>% ungroup() %>% mutate(Dap = pi *
(estimate/(4 * F * A * PYOconcMperCm3))^2) %>% mutate(DapStdError = pi *
(std.error/(4 * F * A * PYOconcMperCm3))^2)
dapQidaB <- left_join(lmBsubsetQ, idaBintSumm, by = c("PHZadded",
"PHZaddedInt", "condition", "rep")) %>% ungroup() %>% mutate(Dap = pi *
(estimate/(2 * F * PYOconcMperCm3))^2) %>% mutate(DapStdError = pi *
(std.error/(4 * F * A * PYOconcMperCm3))^2)
ggplot(dapQidaA %>% filter(term == "t0.5"), aes(x = factor(PHZaddedInt),
y = Dap, fill = condition)) + geom_col(position = "dodge",
color = "black") + geom_errorbar(aes(ymin = Dap - DapStdError,
ymax = Dap + DapStdError), position = "dodge") + labs(title = "",
x = "[PYO] soak (uM)")
Now the transfer step shows a lower Dap than the soak step, which makes more sense. However, the estimates are at least an order of magnitude lower than the current estimate above.
ggplot(dapQidaB %>% filter(term == "t0.5"), aes(x = factor(PHZaddedInt),
y = Dap, fill = condition)) + geom_col(position = "dodge",
color = "black") + geom_errorbar(aes(ymin = Dap - DapStdError,
ymax = Dap + DapStdError), position = "dodge") + labs(title = "",
x = "[PYO] soak (uM)")
Let’s look at all of our different estimates of Dap compared to Dphys (cm^2/s).
tibble(method = c("Scott Potential Step Current", "Fernanda Potential Step",
"Scott GC vs SWV", "Scott Potential Step Charge", "Scott Dphys estimate (z=10um)"),
soak = c(5e-07, 4.7e-06, 8.5e-06, 5.6e-08, NA), transfer = c(2e-06,
7e-07, 1.4e-06, 4.5e-08, 2.5e-09)) %>% kable(digits = 12,
format = "html") %>% kable_styling(bootstrap_options = "striped",
full_width = FALSE)
method | soak | transfer |
---|---|---|
Scott Potential Step Current | 5.0e-07 | 2.0e-06 |
Fernanda Potential Step | 4.7e-06 | 7.0e-07 |
Scott GC vs SWV | 8.5e-06 | 1.4e-06 |
Scott Potential Step Charge | 5.6e-08 | 4.5e-08 |
Scott Dphys estimate (z=10um) | NA | 2.5e-09 |
In some regards the estimates of Dap are pretty consistent. This could be encouraging, because none of the Dap measurements are as low as the Dphys measurement. It is important to remember that the parameters effect these estimates a lot, so to believe these differences we need to think about how we can be completely confident in our choice of parameters.
As mentioned in the last notebook, I think the best way to do this is to repeat some of these measurements on an blank IDA (in Vitro). We should be able to get reasonable values for PYO diffusing in solution (Dap=Dphys).