options(htmltools.dir.version = FALSE) knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE, fig.showtext = TRUE) # Libraries library(tidyverse) library(xaringanthemer) library(here) library(HybridGIS) library(patchwork) library(amstools) library(gt) library(knitr) library(broom) # Colors dark_blue <- "#041e42" light_blue <- "#00a9e0" medium_blue <- "0069b1" aqua_blue <- "#00b7bd" cool_gray_11 <- "#53565a" cool_gray_4 <- "#bbbcbc" background <- "#e6e7e8" style_mono_light( # colors base_color = dark_blue, white_color = lighten_color("#d9d9d6", .5), # primary_color = background, # secondary_color = dark_blue, # fonts header_font_google = google_font("Montserrat", "800"), text_font_google = google_font("Red Hat Display", "500") ) style_extra_css( css = list( ".title-slide" = list( "background-image" = "url(images/whoi_seal_title_bkgnd.jpg)", "background-size" = "cover" ) ), append = TRUE, heading = "Extra CSS" )
.pull-left[ Inject CO2 gas onto Ti target Sputter with Cs+ Dissociate molecules Produce C- ions (and O-, CH-, OH-, CO-...) * Extract C- beam ]
.pull-right[
.right[Gas density in HGIS Cathode
(Salazar, 2013)] ]
???
introduction onto Ti surface in target recess
Predicted chemistry/physics of decomposition and ionization
Plasma in target recess? Blue glow.
.pull-left[
.pull-right[
methods <- tribble(~" ", ~"Graphite", ~"CFAMS ECR GIS", ~"Hybrid GIS", "Continous flow", "No", "Yes", "No", "Measurement time", "30 min", "5 min", "15-30 min", "Precision", "0.3%", "1%", "1%", "Background", "0.1 pmc", "1 pmc", "1 pmc") methods %>% kable(format = "html", caption = "Comparison of AMS methods")
]
???
.pull-left[
.center[]
.right[MCSNICS source with gas arm]
]
.pull-right[
.center[
]
]
???
.pull-left[
.pull-right[
.right[NEC hybrid target cutaway]
]
???
CAMS and ETH have both done a lot of target geometry research.
In the end, compromise between simplicity and function.
.pull-left[
.pull-right[
read.csv(here("data/14Nov2017LPtest.csv"), skip = 4) %>% mutate(flow = flowcalc(supply/10, 2E-5, 1.49E-5, 1.58)) %>% ggplot(aes(flow, current)) + geom_smooth(se = FALSE, span = 0.7, color = "#00b7bd") + geom_point(size = 4, color = "#0069b1") + labs(title = "Ion current and gas flow", x = bquote('CO'[2]~'Flow (μl/min)'), y = "12C current (μA)") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 16, background_color = "#ffffff")
]
???
Bottle gas allowed testing full range of flows
Precision not accuracy: modern gas normalized to itself
.pull-left[ Adapter between CO2 producers and source.
Goal: constant flow of CO2 to source at ideal rate.
.pull-right[
]
???
Pressure of CO2 in inlet controls flow in capillary
P = flow = current
1/viscosity = flow
Gas inlet controls flow of CO2 to source by varying pressure, volume, and gas composition.
increase partial pressure of CO2 by dilution?
Dilution Makes moving small amounts of gas easier by increasing pressure
Source doesn't care about helium as long as it gets its CO2
Introduce steady, reproducible current as key to data quality
Constant flow of CO2 to source produces constant currents, which reduces ratio variability.
.pull-left[
]
.pull-right.center[
.right[Dual-bellows closed inlet] ]
???
.pull-left[
.right[Closed inlet schematic]
]
.pull-right[
data <- get_hgis_data(here("data/USAMS052318RHIGS.txt")) %>% mutate(Num = ifelse(Pos == 15, "S", "U")) stdrat <- mean(data$cor1412he[data$Num == "S" & data$outlier == FALSE]) data_523 <- mutate(data, normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS053018R.txt"), as.Date("2018-05-30")) %>% mutate(Pos = ifelse(Sample.Name == "OX-II", 6, Pos), Num = ifelse(Pos == 5, "S", "U")) stdrat <- mean(data$cor1412he[data$Num == "S"]) data_530 <- mutate(data, normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS060618R.txt")) %>% filter(Pos != 1, #remove spurious pos1 runs !(Pos == 3 & X13.12he > 0.011)) %>% #remove solid OX-I fliers mutate(Num = ifelse(Pos == 5, "S", "U")) stdrat <- mean(data$cor1412he[data$Num == "S" & data$outlier == FALSE]) data_606 <- mutate(data, normFm = norm_gas(cor1412he, stdrat)) dbd <- rbind(data_523, data_530, data_606) std <- getStdTable() dbds <- sum_hgis(dbd) %>% mutate(rec_num = case_when(str_detect(Sample.Name, "Live") ~ 101730, Sample.Name == "OX-I" ~ 34148, Sample.Name == "OX-II" ~ 34149, str_starts(Sample.Name, "C1") ~ 83028)) %>% left_join(select(std, rec_num, fm_consensus), by = "rec_num") %>% mutate(fm_consensus = case_when(rec_num == 101730 ~ 1.0398, rec_num == 72446 ~ 0.0013, TRUE ~ fm_consensus), fm_diff = mean - fm_consensus) dbds %>% filter(Cur < 10) %>% ggplot(aes(fm_consensus, fm_diff)) + geom_hline(yintercept = 0) + geom_pointrange(aes(ymin = fm_diff - merr, ymax = fm_diff + merr), position = position_dodge2(width = .1), size = .6, color = "#0069b1") + labs(title = "Closed inlet", x = "Expected Fm", y = "Fm difference") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 16, background_color = "#ffffff")
]
.pull-left[
Open inlets maintain constant atmospheric pressure, producing constant1 flow to source.
]
.pull-right.center[
.right[Open inlet setup]
]
.footnote[[1] flow varies by composition due to differing viscosity]
???
.pull-left[
]
.pull-right[
.center[]
.right[Automated gas handling system (Roberts, 2013)
]
]
???
table: comparison of carbonate methods figure: DAG graph of process
.pull-left[
Helium displacement of CO2 from vial.
Monitor currents and ratios as pCO2 decreases.
.pull-right[
data <- get_hgis_data(here("data/USAMS040121R.txt"), as.Date("2021-04-02")) %>% filter(Pos == 5) %>% mutate(time = cum_acqtime/60, co2flow = concCO2(time, r = 244) * 30) #30ul/min # flow vs time subplot flow_time <- data.frame(x = 0:125) %>% ggplot(aes(x)) + stat_function(fun=function(x) concCO2(x, r = 244) * 30, aes(color = "CO2"), size = 1.5) + stat_function(fun=function(x) 30 - (concCO2(x, r = 244) * 30), aes(color = "Helium"), size = 1.5) + scale_color_manual("Gas", values = c("#00b7bd", "#b7bf10")) + labs(title = "Gas flows to source", subtitle = "7mL vial, 244μL/min helium, 30μL/min to source", y = "Gas flow (μL/min)") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 18, background_color = "#ffffff") + theme(axis.title.x = element_blank(), axis.text.x = element_blank(), legend.position = c(0.87, 0.65), legend.background = element_rect(fill = "white", color = "black")) # current vs time subplot cur_time <- ggplot(data, aes(time, he12C)) + geom_smooth(span = .4, se=FALSE, color = "#00b7bd") + geom_point(size = 3, color = "#0069b1") + xlim(0, 125) + labs(title = "Ion current", x = "Time (min)", y = "12C current (μA)") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 18, background_color = "#ffffff") flow_time / cur_time
]
.pull-left[
]
.pull-right[
ggplot(data, aes(co2flow, he12C)) + geom_smooth(span = .3, se=FALSE, color = "#00b7bd") + geom_point(size = 3, color = "#0069b1") + labs(title = "Vial dilution current", subtitle = "250μL/min displacement, 30μl/min delivery", x = bquote('CO'[2]~'Flow (μl/min)'), y = "12C current (μA)") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 16, background_color = "#ffffff")
]
???
compare to first tests
Note that our flat-topped current curve allows variable flow of the vial system. MICADAS has a more pronounced peak so this may not work as well. (Check papers for peak shape)
Also note current dependent ratio (blank) as a complicating factor
Lead into blank on next slide
data<- get_hgis_data(here("data/USAMS101320R.txt"), as.Date("2020-11-17")) %>% mutate(Num = ifelse(Pos %in% c(2, 4), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data1013 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS120320R.txt"), as.Date("2020-12-04")) %>% mutate(Num = ifelse(Pos %in% c(2, 4), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data1204 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data1211 <- get_hgis_data(here("data/USAMS120320R.txt")) %>% filter(Pos %in% 1:4 | as.Date(ts) == "2020-12-11") data <- get_hgis_data(here("data/USAMS120320R.txt"), as.Date("2020-12-18")) %>% filter(Pos != 0) %>% mutate(Num = ifelse(Pos %in% c(2, 4), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data1218 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data0108 <- get_hgis_data(here("data/USAMS120320R.txt"), as.Date("2021-01-08")) data <- get_hgis_data(here("data/USAMS020521R.txt")) %>% mutate(Num = ifelse(Pos %in% c(2, 4), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data205 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS030421R.txt"), as.Date("2021-03-05")) %>% mutate(Num = ifelse(Pos %in% c(2, 4), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data304 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS040121R.txt"), as.Date("2021-04-09")) %>% mutate(Num = ifelse(Pos %in% c(22, 24), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data409 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) data <- get_hgis_data(here("data/USAMS041521R.txt"), as.Date("2021-04-16")) %>% mutate(Num = ifelse(Pos %in% c(22, 24), "S", ifelse(Num == "S", "U", Num))) stdrat <- mean(data$cor1412he[data$Num == "S" & !data$outlier]) data415 <- data %>% mutate(normFm = norm_gas(cor1412he, stdrat)) std <- getStdTable() data <- rbind(data1013, data1204, data1211, data1218, data205, data304, data409, data415) %>% mutate(rec_num = case_when(str_detect(Sample.Name, "LiveGas") ~ 101730, str_starts(Sample.Name, "C-1") ~ 83028, str_starts(Sample.Name, "C1") ~ 83028, str_starts(Sample.Name, "TIRI-F") ~ 2138, str_starts(Sample.Name, "TIRI-I") ~ 17185, str_starts(Sample.Name, "C-2") ~ 1082, str_starts(Sample.Name, "C2") ~ 1082, str_starts(Sample.Name, "NOSAMS") ~ 38809, str_detect(Sample.Name, "DeadGas") ~ 72446)) %>% left_join(select(std, rec_num, fm_consensus), by = "rec_num") %>% mutate(fm_consensus = case_when(rec_num == 101730 ~ 1.0398, rec_num == 72446 ~ 0.0013, TRUE ~ fm_consensus)) # Summarize by target ds <- sum_hgis(data) %>% filter((str_detect(Sample.Name, "LiveGas") & Cur > 1) | (str_detect(Sample.Name, "DeadGas") & Cur > 1) | str_detect(Sample.Name, "Blank")) %>% mutate(Gas = case_when(str_detect(Sample.Name, "LiveGas") ~ "LiveGas", str_detect(Sample.Name, "DeadGas") ~ "DeadGas", TRUE ~ "Blank"), Cur_inv = 1/Cur) # Fit linear model fits <- ds %>% ungroup() %>% nest(data = -Gas) %>% mutate(fit = map(data, ~lm(mean ~ Cur_inv, data = .x)), tidied = map(fit, tidy)) %>% unnest(tidied) blankfit <- fits %>% select(Gas, term, estimate) %>% pivot_wider(names_from = c(Gas, term), values_from = estimate) %>% mutate(inv_m_blank = -(`DeadGas_(Intercept)` - `LiveGas_(Intercept)`)/(DeadGas_Cur_inv - LiveGas_Cur_inv), Fm_blank = `DeadGas_(Intercept)` + DeadGas_Cur_inv * inv_m_blank, m_blank = 1/inv_m_blank) # Modeled Fm and mass of blank blank_val <- blankfit %>% select(Fm_blank, m_blank)
.pull-left[
$$I_{sample} + I_{blank} = I_{meas}$$ $$Fm_mI_m=Fm_bI_b+Fm_sI_s$$
$$Fm_s = Fm_m + \frac{I_b(Fm_m-Fm_b)}{I_m-I_b}$$
0.1 μA blank current with 10 μA total = 1% blank
.pull-left[
r format(blank_val$Fm_blank, digits = 3)
r format(blank_val$m_blank, digits = 3)
μA
].pull-right[
r ds %>% filter(Gas == "Blank") %>% pull(mean) %>% mean() %>% format(digits = 3)
r ds %>% filter(Gas == "Blank") %>% pull(Cur) %>% mean() %>% format(digits = 3)
μA
]
].pull-right[
xl <- 8 livefm_cur <- data %>% filter(!(he12C < 2.5 & normFm > 1.05) & fm_consensus > 0.9) %>% ggplot(aes(he12C, normFm)) + geom_point(size = 2, color = "#0069b1") + xlim(0, xl) + ylim(.8, 1.2) + ggtitle("Ratio vs current") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 18, background_color = "#ffffff") + theme(axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.x = element_blank()) deadfm_cur <- data %>% filter(fm_consensus < 0.1 & normFm < 0.3 & Sample.Name != "DilDeadGas6") %>% ggplot(aes(he12C, normFm)) + geom_point(size = 2, color = "#00a9e0") + xlim(0, xl) + ylim(0, .4) + xlab("12C Current (μA)") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 18, background_color = "#ffffff") (livefm_cur / deadfm_cur) + ylab("Fraction modern") + theme(axis.title.y = element_text(hjust=-1))
# Plot of data and model with measured blanks ggplot(ds, aes(Cur_inv, mean, color = Gas)) + geom_abline(slope = blankfit$LiveGas_Cur_inv, intercept = blankfit$`LiveGas_(Intercept)`, color = "#0069b1") + geom_abline(slope = blankfit$DeadGas_Cur_inv, intercept = blankfit$`DeadGas_(Intercept)`, color = "#00a9e0") + geom_pointrange(aes(ymin = mean - merr, ymax = mean + merr), size = .3) + scale_color_manual("Gas", values = c("#b7bf10", "#00a9e0", "#0069b1")) + xlim(0, 10) + labs(title = "Modeled blank", subtitle = "Fits cross at Fm and current of blank", x = "Inverse 12C Current (μA-1)", y = "Fraction modern") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 16, background_color = "#ffffff") + theme(legend.position = "none", legend.background = element_rect(fill = "white", color = "black"))
]
???
Blank = Ion current not from sample gas
Targets alone and targets + helium (fm and current)
carb_data <- map_dfr(list.files(here("data_analysed"), full.names = TRUE), read_csv) %>% filter(!is.na(rec_num), Cur > 2) %>% mutate(fm_diff = mean - fm_consensus, fm_sigma = sigma(mean, fm_consensus, merr), fmbc_diff = fm_lbc - fm_consensus, fmbc_sigma = sigma(fm_lbc, fm_consensus, merr))
.pull-left[ If standards, blanks, and unknowns have the same blank and current, current drops out.
$$Fm_s = Fm_m - Fm_{blk}\frac{Fm_{std} - Fm_m}{Fm_{std}}$$
r carb_data %>% filter(fm_consensus < 0.1 & Cur > 2) %>% pull(mean) %>% mean() %>% format(digits = 3)
]
.pull-right[
carb_data %>% filter(fm_consensus < 0.1 & Cur > 2) %>% ggplot(aes(Cur, mean)) + geom_errorbar(aes(ymin = mean - merr, ymax = mean + merr)) + geom_errorbarh(aes(xmin = Cur - Cur.sd, xmax = Cur + Cur.sd)) + geom_point(aes(color = wheel), size = 3) + scale_color_manual("wheel", values = c("#b7bf10", "#00a9e0", "#0069b1")) + labs(title = "Blanks", x = "12C Current (μA)", y = "Fraction modern") + theme_classic() + theme_xaringan(title_font_size = 20, text_font_size = 16, background_color = "#ffffff") + theme(legend.position = c(0.82, 0.76), legend.background = element_rect(fill = "white", color = "black"))
]
???
We'll see what fit looks like on next slide.
Current/target dependent? Constant blanks and currents allow single mass independent correction
3 ways to control blank
Reduce blank - better cleaning, presputtering
Hold standard, blank, unknown current constant and use current independent correction (linear correction based on dead sample Fm)
* Determine Fm and current of blank and use current dependent correction (current balance model)
Idea: use data from dillution runs of live and dead gas to create curves
It's relatively easy to produce runs with terrible current, but usually not good for blank as caused by other issues.
.left-column[
r format(mean(carb_data$fmbc_diff), digits = 1)
r format(sd(carb_data$fmbc_diff), digits = 2)
]
.right-column[ .right[
ps <- 0.3 dw <- 0.03 yl <- .1 secs <- ggplot(carb_data, aes(fm_consensus, fm_diff, color = Cur)) + geom_hline(yintercept = 0) + geom_smooth(method = "lm", se = FALSE) + geom_pointrange(aes(ymin = fm_diff - merr, ymax = fm_diff + merr), position = position_dodge2(width = dw), size = ps,) + lims(x = c(-0.05, 1.1), y = c(-yl, yl)) + labs(title = "Difference from expected Fm", subtitle = "Uncorrected", colour = "Cur (μA)", y = "Fm difference") + theme_classic() + theme_xaringan(title_font_size = 18, text_font_size = 16, background_color = "#ffffff") + theme(axis.title.x = element_blank(), axis.text.x = element_blank(), legend.direction = "horizontal", legend.position = c(.80, 1), legend.justification = "bottom") secs_lbc <- ggplot(carb_data, aes(fm_consensus, fmbc_diff, color = Cur)) + geom_hline(yintercept = 0) + geom_smooth(method = "lm", se = FALSE) + geom_pointrange(aes(ymin = fmbc_diff - merr, ymax = fmbc_diff + merr), position = position_dodge2(width = dw), size = ps) + lims(x = c(-0.05, 1.1), y = c(-yl, yl)) + labs(subtitle = "Blank corrected", x = "Fm expected", y = "Fm difference") + theme_classic() + theme_xaringan(title_font_size = 18, text_font_size = 16, background_color = "#ffffff") + theme(legend.position = "None") secs / secs_lbc
] ]
???
precision, accuracy issues with blank and normalization
.pull-left[
.pull-right[
.right[MICADAS Gas Interface]
]
???
More pronounced current vs. flow curve makes measurement more difficult at variable CO2 flow. Salazar and Szidat are developing an open split interface with EA - squish peak with dead volume
photo: micadas source
NOSAMS has developed a successful method for analyzing radiocarbon in carbonates with an open-split interface delivering CO2 gas to a NEC MCSNICS gas-accepting cesium sputter ion source.
Mark Roberts, Josh Burton, Josh Hlavenka, Al Gagnon, Kalina Gospodinova, Mark Kurz, and the staff of NOSAMS
Karl von Reden
Cam McIntyre, SUERC
Ted Ognibene, CAMS
![]() |
![]() |
![]() |
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.