Title: | Tools for Conducting and Analyzing Respirometry Experiments |
---|---|
Description: | Provides tools to enable the researcher to more precisely conduct respirometry experiments. Strong emphasis is on aquatic respirometry. Tools focus on helping the researcher setup and conduct experiments. Functions for analysis of resulting respirometry data are also provided. This package provides tools for intermittent, flow-through, and closed respirometry techniques. |
Authors: | Matthew A. Birk [aut, cre] |
Maintainer: | Matthew A. Birk <[email protected]> |
License: | GPL-3 |
Version: | 2.0.1 |
Built: | 2024-11-25 16:28:46 UTC |
Source: | CRAN |
Predicts the values of any inputted biological parameter (e.g. MO2, Pcrit) at a new temperature based on empirical measurements at a range of temperatures. Data can be fit to a temperature-dependence curve using either Q10
or calc_E
. By default, the predicted values are also plotted alongside the inputted data to allow the user to assess the quality of the fit.
adj_by_temp( meas_temp, meas_x, temp_new, method = "Q10", Q10, E, show_coef = FALSE, plot_fit = TRUE )
adj_by_temp( meas_temp, meas_x, temp_new, method = "Q10", Q10, E, show_coef = FALSE, plot_fit = TRUE )
meas_temp |
a vector of temperature values (°C) corresponding to |
meas_x |
a vector of biological values (e.g. MO2, Pcrit) corresponding to |
temp_new |
a vector of temperature values (°C) at which new values of "x" should be predicted. |
method |
which method for calculcating temperature-dependency should be used? Options are "Q10" (default) and "E". If either |
Q10 |
(optional). A Q10 value to be used for predicting new values of "x". If |
E |
(optional). An E value to be used for predicting new values of "x". If |
show_coef |
logical. Should the temperature-dependency coefficient (i.e. the numeric value of either Q10 or E) be returned alongside the new values of "x"? Default is |
plot_fit |
logical. Should a plot be displayed showing how well the new "x" values fit with the inputted data? Default is |
If show_coef = FALSE
(default), then a numeric vector of new values of "x" are returned.
If show_coef = TRUE
, then a list of new values and the temperature-dependency coefficient are returned.
Matthew A. Birk, [email protected]
# I measured Pcrit at four different temperatures. What is the Pcrit at an # intermediate temperature? adj_by_temp(meas_temp = c(5, 10, 15, 20), meas_x = c(3.1, 6.3, 7, 8.4), temp_new = 18) # If requested values exceed the inputted temperature range, a message is reported. # Biology cannot go on forever like the math can. adj_by_temp(meas_temp = c(10, 15, 20, 25), meas_x = c(4.8, 6, 12.3, 13.6), temp_new = 0:30)
# I measured Pcrit at four different temperatures. What is the Pcrit at an # intermediate temperature? adj_by_temp(meas_temp = c(5, 10, 15, 20), meas_x = c(3.1, 6.3, 7, 8.4), temp_new = 18) # If requested values exceed the inputted temperature range, a message is reported. # Biology cannot go on forever like the math can. adj_by_temp(meas_temp = c(10, 15, 20, 25), meas_x = c(4.8, 6, 12.3, 13.6), temp_new = 0:30)
The oxygen supply capacity () is a species- and temperature-specific value quantifying an animal's ability to extract oxygen from the ambient medium to support its metabolism (e.g. umol O2 / g / hr / kPa). This function calculates
based on the single highest
(MO2/PO2) value in the dataset. If there are outliers that make this prohibitive, consider setting a threshold MO2 value with
mo2_threshold
.
calc_alpha(po2, mo2, avg_top_n = 1, MR = NULL, mo2_threshold = Inf)
calc_alpha(po2, mo2, avg_top_n = 1, MR = NULL, mo2_threshold = Inf)
po2 |
a vector of PO2 values. |
mo2 |
a vector of metabolic rate values. Must be the same length and corresponding to |
avg_top_n |
a numeric value representing the number of top |
MR |
a vector of values for the metabolic rate at which |
mo2_threshold |
a single numeric value above which |
Returns a list of 1) alpha, 2) a list of the PO2, MO2, and alpha0 value(s) where alpha was reached (the number of observations averaged is set by avg_top_n
), and 3) the Pcrit at a metabolic rate of MR
.
Matthew A. Birk, [email protected]
Seibel, B. A., A. Andres, M. A. Birk, A. L. Burns, C. T. Shaw, A. W. Timpe, C. J. Welsh. 2021. “Oxygen supply capacity breathes new life into the critical oxygen partial pressure (Pcrit).” Journal of Experimental Biology.
mo2_data <- read.csv(system.file('extdata', 'mo2_v_po2.csv', package = 'respirometry')) calc_alpha(po2 = mo2_data$po2, mo2 = mo2_data$mo2, MR = 1.5) # MR set to 1.5 to capture the # Pcrit corresponding to some of the lowest MO2 values recorded (something close to SMR). # extract the alpha0 values that were averaged together sapply(calc_alpha(po2 = mo2_data$po2, mo2 = mo2_data$mo2, MR = 1.5, avg_top_n = 3)$alpha_obs, function(i) i[3])
mo2_data <- read.csv(system.file('extdata', 'mo2_v_po2.csv', package = 'respirometry')) calc_alpha(po2 = mo2_data$po2, mo2 = mo2_data$mo2, MR = 1.5) # MR set to 1.5 to capture the # Pcrit corresponding to some of the lowest MO2 values recorded (something close to SMR). # extract the alpha0 values that were averaged together sapply(calc_alpha(po2 = mo2_data$po2, mo2 = mo2_data$mo2, MR = 1.5, avg_top_n = 3)$alpha_obs, function(i) i[3])
For most organisms, metabolic rate does not scale linearly, but rather according to a power function: . This function estimates the scaling coefficient,
b
, and normalization constant, b0
, given MO2s from different sized individuals.
calc_b(mass, MO2, method = "nls", plot = "linear", b0_start = 1)
calc_b(mass, MO2, method = "nls", plot = "linear", b0_start = 1)
mass |
a vector of animal masses. |
MO2 |
a vector of metabolic rates. |
method |
a string defining which method of calculating scaling coefficients to use. Default is "nls", which utilizes a nonlinear least squares regression. If this does not fit your data well, "lm" may also be used, which calculates a linear regression of log10( |
plot |
a string defining what kind of plot to display. "linear" for linear axes, "log" for log10-scale axes, and "none" for no plot. Default is "linear". |
b0_start |
a single numeric value as the starting point for |
where b0
is species-specific normalization constant, M
is mass and b
is the scaling coefficient.
Returns a list of 1) the b
value, 2) a vector of b0
values corresponding to the input MO2
values, and 3) an average b0
that can be used for summarizing the relationship with an equation.
Matthew A. Birk, [email protected]
# Simple example mass <- c(1, 10, 100, 1000, 40, 4, 400, 60, 2, 742, 266, 983) # made up values MO2 <- mass ^ 0.65 + rnorm(n = length(mass)) # make up some data calc_b(mass = mass, MO2 = MO2) # How about some mass-specific MO2s? msMO2 <- mass ^ -0.25 + rnorm(n = length(mass), sd = 0.05) calc_b(mass = mass, MO2 = msMO2) calc_b(mass = mass, MO2 = msMO2, plot = "log")
# Simple example mass <- c(1, 10, 100, 1000, 40, 4, 400, 60, 2, 742, 266, 983) # made up values MO2 <- mass ^ 0.65 + rnorm(n = length(mass)) # make up some data calc_b(mass = mass, MO2 = MO2) # How about some mass-specific MO2s? msMO2 <- mass ^ -0.25 + rnorm(n = length(mass), sd = 0.05) calc_b(mass = mass, MO2 = msMO2) calc_b(mass = mass, MO2 = msMO2, plot = "log")
An E value is a relatively recent metric to parameterize the temperature-sensitivity of a biological rate (MO2). It is similar conceptually (but not numerically) to Q10.
calc_E(x, temp)
calc_E(x, temp)
x |
a numeric vector of rate values (e.g. MO2) or any other values (e.g. Pcrit). |
temp |
a numeric vector of temperature values (in Celsius). |
E is the slope of the relationship between -ln(x)
and 1/(kB T)
, where kB
is the Boltzmann constant expressed in eV/K.
Matthew A. Birk, [email protected]
Deutsch, Curtis et al. 2015. “Climate Change Tightens a Metabolic Constraint on Marine Habitats.” Science 348(6239): 1132–35.
calc_E(x = c(1, 2, 3), temp = c(10, 20, 30))
calc_E(x = c(1, 2, 3), temp = c(10, 20, 30))
Calculates metabolic rate (MO2) given O2 measurements over time. Oxygen measurements are split into bins and MO2s are calculated from each bin (unless bin_width
is set to 0
). The bin_width
parameter defines the width of the bins in timed intervals (e.g. 15 minutes). Linear regressions are fit through each bin and the calculated MO2 is returned as the slope of the change in O2 over time.
calc_MO2( duration, o2, o2_unit = "percent_a.s.", bin_width, vol, temp = 25, sal = 35, atm_pres = 1013.25, time, pH, good_data = TRUE ) calc_mo2( duration, o2, o2_unit = "percent_a.s.", bin_width, vol, temp = 25, sal = 35, atm_pres = 1013.25, time, pH, good_data = TRUE )
calc_MO2( duration, o2, o2_unit = "percent_a.s.", bin_width, vol, temp = 25, sal = 35, atm_pres = 1013.25, time, pH, good_data = TRUE ) calc_mo2( duration, o2, o2_unit = "percent_a.s.", bin_width, vol, temp = 25, sal = 35, atm_pres = 1013.25, time, pH, good_data = TRUE )
duration |
numeric vector of the timepoint for each observation (minutes). |
o2 |
numeric vector of O2 observations. |
o2_unit |
a string describing the unit used to measure |
bin_width |
numeric or data frame.
|
vol |
volume of the respirometer (liter). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
time |
(optional). Numeric vector of timestamp observations. |
pH |
(optional). Numeric vector of pH observations. |
good_data |
logical vector of whether O2 observations are "good" measurements and should be included in analysis. Linear regressions will not be fit over bins that include "bad" data. Bins will be split at bad data points. Default is that all observations are |
A data frame is returned:
Mean duration of the bin (minutes).
Range of duration timepoints in the bin.
Exists only if the parameter time
has values. Mean timestamp of the bin.
Exists only if the parameter time
has values. Range of timestamps in the bin.
Mean temperature of the bin.
Exists only if the parameter pH
has values. Mean pH of the bin. Averaged using mean_pH()
.
Mean O2 value of the bin in the unit chosen by o2_unit
).
Range of O2 values in the bin.
Metabolic rate (umol O2 / hour).
Coefficient of determination for the linear regression fit to calculate MO2.
Number of observations in the bin.
Whole-animal MO2 is returned. If mass-specific MO2 is desired, the output from calc_MO2
can be divided by the animal's mass.
No matter what unit of oxygen partial pressure or concentration measurement you put into the function as o2_unit
, the output in the MO2 column is always expressed in umol O2 / hour. This is because there is a vast variety of units for which people prefer to report dissolved oxygen levels, but most physiologists are at least unified in reporting metabolic rate as umol O2 per hour. If you prefer to report MO2 as mg O2 per hour, for example, you can always do something like:
conv_resp_unit(df$MO2, from = 'umol_O2 / hr', 'mg_O2 / hr')
If only beginning and ending O2 observations are known, consider using closed
. Both functions will work fine, but closed
is simpler.
Matthew A. Birk, [email protected]
make_bins
, calc_b
, closed
, scale_MO2
, conv_resp_unit
# get O2 data file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') o2_data <- na.omit(import_witrox(file, split_channels = TRUE)$CH_4) # calculate MO2 (mo2_5_min <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = 5, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL)) # what if measurements from the 10 to 12 minute marks can't be trusted? bad_data = o2_data$DURATION >= 10 & o2_data$DURATION <= 12 (mo2_5_min <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = 5, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL, good_data = !bad_data)) # easily make a Pcrit plot plot(mo2_5_min$O2_MEAN, mo2_5_min$MO2) # I want to express MO2 in mg per min instead. (mo2_5_min$MO2 <- conv_resp_unit(value = mo2_5_min$MO2, from = 'umol_O2 / hr', to = 'mg_O2 / min')) # just endpoint measurement: calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = Inf, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL) # In my trial, observations above 77% air saturation were really noisy, but much less noisy at # lower O2 values. I want to adjust my bin width based on the PO2 to obtain the best balance of # resolution and precision throughout the whole trial. Below 77% a.s., use 4 minute bins. Above # 77% a.s. use 10 minute bins. bins = data.frame(o2 = c(77, 100), width = c(4, 10)) calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = bins, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL)
# get O2 data file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') o2_data <- na.omit(import_witrox(file, split_channels = TRUE)$CH_4) # calculate MO2 (mo2_5_min <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = 5, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL)) # what if measurements from the 10 to 12 minute marks can't be trusted? bad_data = o2_data$DURATION >= 10 & o2_data$DURATION <= 12 (mo2_5_min <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = 5, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL, good_data = !bad_data)) # easily make a Pcrit plot plot(mo2_5_min$O2_MEAN, mo2_5_min$MO2) # I want to express MO2 in mg per min instead. (mo2_5_min$MO2 <- conv_resp_unit(value = mo2_5_min$MO2, from = 'umol_O2 / hr', to = 'mg_O2 / min')) # just endpoint measurement: calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = Inf, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL) # In my trial, observations above 77% air saturation were really noisy, but much less noisy at # lower O2 values. I want to adjust my bin width based on the PO2 to obtain the best balance of # resolution and precision throughout the whole trial. Below 77% a.s., use 4 minute bins. Above # 77% a.s. use 10 minute bins. bins = data.frame(o2 = c(77, 100), width = c(4, 10)) calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = bins, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL)
Calculates Pcrit (commonly understood as the threshold below which oxygen consumption rate can no longer be sustained) based on paired PO2 and MO2 values. Five Pcrit metrics are returned using many of the popular techniques for Pcrit calculation: the traditional breakpoint metric (broken stick regression), the nonlinear regression metric (Marshall et al. 2013), the sub-prediction interval metric (Birk et al. 2019), the alpha-based Pcrit method (Seibel et al. 2021), and the linear low O2 (LLO) method (Reemeyer & Rees 2019). To see the Pcrit values plotted, see plot_pcrit
.
calc_pcrit( po2, mo2, mo2_data, method = "Breakpoint", avg_top_n = 1, level = 0.95, iqr = 1.5, NLR_m = 0.065, MR = NULL, mo2_threshold = Inf, return_models = FALSE )
calc_pcrit( po2, mo2, mo2_data, method = "Breakpoint", avg_top_n = 1, level = 0.95, iqr = 1.5, NLR_m = 0.065, MR = NULL, mo2_threshold = Inf, return_models = FALSE )
po2 |
a vector of PO2 values. Any unit of measurement should work, but the NLR calculation was optimized using kPa. If the NLR metric is giving you trouble, try converting to kPa using |
mo2 |
a vector of metabolic rate values. Must be the same length and corresponding to |
mo2_data |
for convenience, the output of |
method |
Over the years, many different methods of analysis have been proposed to quantify Pcrit. You must choose one of the following: Alpha, Breakpoint (default), LLO, NLR, Sub_PI, All. If in doubt, try "All". |
avg_top_n |
applies to the |
level |
applies to the |
iqr |
applies to the |
NLR_m |
applies to the |
MR |
applies to the |
mo2_threshold |
applies to the |
return_models |
logical. Should a list of model parameters be returned along with the converged Pcrit values? Default is |
Alpha is calculated from calc_alpha
and the Pcrit corresponding to MR
is returned. This determine's the animal's oxygen supply capacity and calculates the Pcrit at any given metabolic rate of interest. If no MR
is provided, then it defaults to the mean MO2 value from the oxyregulating portion of the curve (as defined by the broken-stick regression).
Data are fit to a broken-stick regression using segmented
.
A subset of observations are chosen only from those with an MO2 < MR
. Then, a linear model is fit through the observations and Pcrit is calculated as the PO2 at which the line reaches MR
.
Data are fit to the following functions: Michaelis-Menten, Power, Hyperbola, Pareto, and Weibull with intercept. Following the method developed by Marshall et al. 2013, the function that best fits the data (smallest AIC) is chosen and the Pcrit is determined as the PO2 at which the slope of the function is NLR_m
(by default = 0.065 following the authors' suggestion).
This metric builds off the Breakpoint
metric and results in a systematically lower Pcrit value. This is useful for applications where it is important to ensure that Pcrit is not being overestimated. It represents a reasonable lower bounded estimate of the Pcrit value for a given trial. Once the Breakpoint
Pcrit is calculated, a 95% prediction interval (can be changed with the level
argument) is calculated around the oxyregulating region (i.e. using PO2 values > breakpoint Pcrit). By default, iqr
provides some filtering of abberant observations to prevent their influence on the calculated prediction interval. Finally, the Sub_PI Pcrit value is returned at the intersection of the oxyconforming line and the lower limit of the oxyregulating prediction interval.
If return_models
is FALSE
(default), a numeric Pcrit value is returned based on method
. If method == "All"
, a named numeric vector of Pcrit values calculated using the Alpha
, Breakpoint
, LLO
, NLR
, and Sub_PI
metrics is returned. If return_models
is TRUE
, then a list of converged Pcrit values, along with breakpoint function parameters, the MR
value used for calculating Pcrit-alpha, a data frame of the "oxyregulating" portion of the curve, and NLR parameters are returned.
Matthew A. Birk, [email protected]
Birk, Matthew A., K.A.S. Mislan, Karen F. Wishner, and Brad A. Seibel. 2019. “Metabolic Adaptations of the Pelagic Octopod Japetella Diaphana to Oxygen Minimum Zones.” Deep-Sea Research Part I 148: 123–31.
Marshall, Dustin J., Michael Bode, and Craig R. White. 2013. “Estimating Physiological Tolerances - a Comparison of Traditional Approaches to Nonlinear Regression Techniques.” Journal of Experimental Biology 216(12): 2176–82.
Reemeyer, Jessica E., and Bernard B. Rees. 2019. “Standardizing the Determination and Interpretation of Pcrit in Fishes.” Journal of Experimental Biology 222(18): jeb210633.
Seibel, B. A., A. Andres, M. A. Birk, A. L. Burns, C. T. Shaw, A. W. Timpe, C. J. Welsh. 2021. “Oxygen supply capacity breathes new life into the critical oxygen partial pressure (Pcrit).” Journal of Experimental Biology.
plot_pcrit
, calc_MO2
, conv_o2
, calc_alpha
raw_data <- system.file('extdata/pcrit_run/', package = 'respirometry') o2_data <- import_pyroscience_workbench(folder = raw_data) mo2_data <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$CH_1_O2, bin_width = 10, vol = 3) calc_pcrit(mo2_data = mo2_data) calc_pcrit(po2 = mo2_data$O2_MEAN, mo2 = mo2_data$MO2, method = 'All', MR = 100)
raw_data <- system.file('extdata/pcrit_run/', package = 'respirometry') o2_data <- import_pyroscience_workbench(folder = raw_data) mo2_data <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$CH_1_O2, bin_width = 10, vol = 3) calc_pcrit(mo2_data = mo2_data) calc_pcrit(po2 = mo2_data$O2_MEAN, mo2 = mo2_data$MO2, method = 'All', MR = 100)
Returns the unknown parameter given 3 of 4 parameters to calculate respiration rate in a closed respirometer. This is useful both for basic closed respirometry setups, and also for the closed measurement phase of intermittent respirometry. Note that this is mainly useful for designing respirometry experiments. If you already have a timeseries dataset of oxygen measurements you want to analyze, use calc_MO2
instead.
closed(MO2, delta_pO2, duration, vol, temp = 25, sal = 35, atm_pres = 1013.25)
closed(MO2, delta_pO2, duration, vol, temp = 25, sal = 35, atm_pres = 1013.25)
MO2 |
whole-animal oxygen consumption rate (umol O2 / hour). |
delta_pO2 |
desired change in pO2 (% air saturation). |
duration |
desired duration to reach |
vol |
volume of the respirometer (liter). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
If there are more than two O2 observations, consider using calc_MO2
.
Matthew A. Birk, [email protected]
# I've read in the literature that my animal has an SMR of 200 umol/h. How large of a # respirometer do I want if I want it to breathe down to 80% air saturation in 30 minutes? closed(MO2 = 200, delta_pO2 = 100 - 80, duration = 30) # returns respirometer volume # I've read in the literature that my animal has an SMR of 1000 umol/h. How long will it take to # breathe down a 50 L respirometer by 10% air saturation? closed(MO2 = 1000, delta_pO2 = 10, vol = 50) # returns the duration to breathe down the O2 # How does animal size affect how long my measurement periods last? mass_range <- seq(100, 400, 50) dur_range <- (closed(MO2 = scale_MO2(mass_1 = 100, MO2_1 = 400, mass_2 = mass_range), delta_pO2 = 20, vol = 10)) plot(mass_range, dur_range, type = 'b') # What is the MO2 if O2 drops 0.44 mg/l in 33 minutes when the respirometer volume is 30 L? closed(delta_pO2 = conv_o2(o2 = 0.44, from = 'mg_per_l', to = 'percent_a.s.'), duration = 33, vol = 30)
# I've read in the literature that my animal has an SMR of 200 umol/h. How large of a # respirometer do I want if I want it to breathe down to 80% air saturation in 30 minutes? closed(MO2 = 200, delta_pO2 = 100 - 80, duration = 30) # returns respirometer volume # I've read in the literature that my animal has an SMR of 1000 umol/h. How long will it take to # breathe down a 50 L respirometer by 10% air saturation? closed(MO2 = 1000, delta_pO2 = 10, vol = 50) # returns the duration to breathe down the O2 # How does animal size affect how long my measurement periods last? mass_range <- seq(100, 400, 50) dur_range <- (closed(MO2 = scale_MO2(mass_1 = 100, MO2_1 = 400, mass_2 = mass_range), delta_pO2 = 20, vol = 10)) plot(mass_range, dur_range, type = 'b') # What is the MO2 if O2 drops 0.44 mg/l in 33 minutes when the respirometer volume is 30 L? closed(delta_pO2 = conv_o2(o2 = 0.44, from = 'mg_per_l', to = 'percent_a.s.'), duration = 33, vol = 30)
Calculates the moles of CO2 gas to be added to a volume of seawater to achieve the desired pCO2. Useful for ocean acidification experiments where CO2 treatments are desired.
co2_add( goal_pco2, start_pH, vol, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
co2_add( goal_pco2, start_pH, vol, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
goal_pco2 |
the desired pCO2 in the water (uatm). |
start_pH |
pH of the water before CO2 is added (total scale). |
vol |
volume of the water (liter). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
moles of CO2 gas to be added to the seawater.
It is assumed that all of the CO2 added dissolves and remains in solution. This can be achieved (almost completely) by bubbling CO2 according to Jokiel et al. 2014.
Matthew A. Birk, [email protected]
Jokiel PL, Bahr KD, Rodgers KS. 2014. Low-cost, high-flow mesocosm system for simulating ocean acidification with CO2 gas. Limnol Oceanogr Methods. 12:313–322.
co2_rate
, flush_carb
, carb
, peri_pump
# I want the 50 L reservoir to have a pCO2 = 1000 uatm. It currently has a pH of 7.88. # How many moles of CO2 gas should be added to the water to reach my desired pCO2? co2_add(goal_pco2 = 1000, start_pH = 7.88, vol = 50)
# I want the 50 L reservoir to have a pCO2 = 1000 uatm. It currently has a pH of 7.88. # How many moles of CO2 gas should be added to the water to reach my desired pCO2? co2_add(goal_pco2 = 1000, start_pH = 7.88, vol = 50)
Calculates the moles of CO2 gas to be added to a seawater reservoir before flushing a respirometer to achieve the desired pCO2 in the respirometer after the flush. Useful for ocean acidification experiments where CO2 treatments are desired.
co2_flush( goal_pco2, resp_pH, resp_vol, flush_pH, flush_vol, flush_remain = 0, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
co2_flush( goal_pco2, resp_pH, resp_vol, flush_pH, flush_vol, flush_remain = 0, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
goal_pco2 |
the desired pCO2 in the respirometer after the flush (uatm). |
resp_pH |
pH inside the respirometer before the flush (total scale). |
resp_vol |
volume of the respirometer (liter). |
flush_pH |
pH of the reservoir water used for flushing before CO2 is added (total scale). |
flush_vol |
volume of the flush reservoir (liter). |
flush_remain |
volume of the flush reservoir that will remain after the flush (liter). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
moles of CO2 gas to be added to the flush reservoir.
It is assummed that the entire reservoir is drained into the respirometer during the flush. It is also assumed that all of the CO2 added dissolves and remains in solution. This can be achieved (almost completely) by bubbling CO2 according to Jokiel et al. 2014.
Matthew A. Birk, [email protected]
Jokiel PL, Bahr KD, Rodgers KS. 2014. Low-cost, high-flow mesocosm system for simulating ocean acidification with CO2 gas. Limnol Oceanogr Methods. 12:313–322.
co2_add
, co2_rate
, flush_carb
, carb
, peri_pump
# I want the respirometer to have a pCO2 = 1000 uatm. It currently has a pH of 7.6 and is 90 L. # If I have a 200 L reservoir with pH = 7.9 seawater, how much CO2 do I need # to add to the flush reservoir? co2_flush(goal_pco2 = 1000, resp_pH = 7.6, resp_vol = 90, flush_pH = 7.9, flush_vol = 200)
# I want the respirometer to have a pCO2 = 1000 uatm. It currently has a pH of 7.6 and is 90 L. # If I have a 200 L reservoir with pH = 7.9 seawater, how much CO2 do I need # to add to the flush reservoir? co2_flush(goal_pco2 = 1000, resp_pH = 7.6, resp_vol = 90, flush_pH = 7.9, flush_vol = 200)
Calculates the moles of CO2 gas to be added to a respirometer intake seawater flow to achieve the desired pCO2 in the respirometer. Useful for ocean acidification experiments where CO2 treatments are desired. Can be used for acclimation before a trial begins or for use with flow-through respirometry.
co2_rate( goal_pco2, init_pH, flow_rate, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25, MO2 = NULL, RQ = 1 )
co2_rate( goal_pco2, init_pH, flow_rate, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25, MO2 = NULL, RQ = 1 )
goal_pco2 |
the desired pCO2 in the respirometer (uatm). |
init_pH |
ambient pH of the intake flow (total scale). |
flow_rate |
rate of water flow into the respirometer (liters / minute). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
MO2 |
(optional) oxygen consumption rate (umol / hr). If defined, the CO2 to be added is reduced to compensate for the CO2 produced by the organism. |
RQ |
(optional) respiratory quotient: ratio of CO2 produced / O2 consumed. Only used if |
moles of CO2 gas to be added to the intake flow per minute.
It is assumed that all of the CO2 added dissolves and remains in solution. This can be achieved (almost completely) by bubbling CO2 according to Jokiel et al. 2014.
Matthew A. Birk, [email protected]
Jokiel PL, Bahr KD, Rodgers KS. 2014. Low-cost, high-flow mesocosm system for simulating ocean acidification with CO2 gas. Limnol Oceanogr Methods. 12:313–322.
co2_add
, flush_carb
, carb
, peri_pump
# I want the respirometer to have a pCO2 = 1000 uatm. How much CO2 per minute do I need # to add to the intake flow if the ambient pH is 8.1 and it is flowing at 3 LPM? co2_rate(goal_pco2 = 1000, init_pH = 8.1, flow_rate = 3)
# I want the respirometer to have a pCO2 = 1000 uatm. How much CO2 per minute do I need # to add to the intake flow if the ambient pH is 8.1 and it is flowing at 3 LPM? co2_rate(goal_pco2 = 1000, init_pH = 8.1, flow_rate = 3)
Ammonia or nitrogen excretion can be measured in a variety of ways. Convert between different measurements.
conv_nh4(n_waste, from = "umol_NH4", to = "all")
conv_nh4(n_waste, from = "umol_NH4", to = "all")
n_waste |
a numeric vector of the ammonia or nitrogen value(s). |
from |
a string describing the unit used to measure
|
to |
a single string either describing the unit to which the conversion should be conducted (options are the same as in |
The sum of NH4+ and NH3 species are considered (i.e. TAN). Conversions are based on relationships and values from the package marelac
.
Matthew A. Birk, [email protected]
conv_nh4(n_waste = 100) conv_nh4(n_waste = 100, from = 'mg_N') conv_nh4(n_waste = 100, from = 'mg_N', to = 'umol_NH4')
conv_nh4(n_waste = 100) conv_nh4(n_waste = 100, from = 'mg_N') conv_nh4(n_waste = 100, from = 'mg_N', to = 'umol_NH4')
Unfortunately, a consensus on the best way to express how much oxygen is in water has not been formed to date. Until then, this function converts between all commonly used forms of dissolved O2 measurements.
conv_o2( o2 = 100, from = "percent_a.s.", to = "all", temp = 25, sal = 35, atm_pres = 1013.25 )
conv_o2( o2 = 100, from = "percent_a.s.", to = "all", temp = 25, sal = 35, atm_pres = 1013.25 )
o2 |
a numeric vector of the O2 value(s). Default is 100. |
from |
a string describing the unit used to measure
|
to |
a single string either describing the unit to which the conversion should be conducted (options are the same as in |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
Conversions are based on relationships and values from the package marelac
which utilizes saturation values from Weiss 1970.
Matthew A. Birk, [email protected]
Weiss R. 1970. The solubility of nitrogen, oxygen, and argon in water and seawater. Deep-Sea Research. 17:721-735.
conv_o2(o2 = 50) conv_o2(o2 = 1:50, from = "umol_per_l", to = "ml_per_l", temp = 10, sal = 0, atm_pres = 1100) conv_o2()[c('mmHg','kPa')]
conv_o2(o2 = 50) conv_o2(o2 = 1:50, from = "umol_per_l", to = "ml_per_l", temp = 10, sal = 0, atm_pres = 1100) conv_o2()[c('mmHg','kPa')]
Converts units of measurement that are joined by " / " or " * ". This function expands upon conv_multiunit
to incorporate O2 unit conversion and seawater volume-mass conversions.
conv_resp_unit( value, from, to, temp = 25, sal = 35, atm_pres = 1013.25, o2_conc_base = "per_l" )
conv_resp_unit( value, from, to, temp = 25, sal = 35, atm_pres = 1013.25, o2_conc_base = "per_l" )
value |
a numeric vector giving the measurement value in its original units. |
from , to
|
a string defining the unit with subunits separated by " / " or " * ". See Details for proper notation regarding O2 and seawater mass/volume. |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
o2_conc_base |
(optional) if converting between pO2 and [O2], should concentrations be "per_l" or "per_kg"? Default is "per_l". |
The O2 units supported by conv_o2
should be appended with "_O2" (e.g. "kPa_O2"; even "percent_o2_O2") and O2 unit concentrations should drop "per_l" or "per_kg" (e.g. "umol_O2"). To designate seawater mass-volume conversion, append the unit with "_seawater" (e.g. "kg_seawater").
Matthew A. Birk, [email protected]
# I read that an animal's MO2 is 1.92 ml O2/kg/min. What is this MO2 in umol O2/g/h? conv_resp_unit(value = 1.92, from = "ml_O2 / kg / min", to = "umol_O2 / g / hr") # Krogh's diffusion coefficient for oxygen through gills can be expressed as ml O2 / mm2 (gill # surface area) / um (gill thickness) / torr (seawater pO2 - blood pO2) / minute at a given # temperature. # To convert to another unit: conv_resp_unit(value = 1e-6, from = "ml_O2 / mm2 / um / torr / min", to = "umol_O2 / cm2 / um / kPa / hr", temp = 20) # Now, with a knowledge of gill morphometrics, seawater pO2, and blood pO2, I can compare # gill diffusion with whole animal MO2.
# I read that an animal's MO2 is 1.92 ml O2/kg/min. What is this MO2 in umol O2/g/h? conv_resp_unit(value = 1.92, from = "ml_O2 / kg / min", to = "umol_O2 / g / hr") # Krogh's diffusion coefficient for oxygen through gills can be expressed as ml O2 / mm2 (gill # surface area) / um (gill thickness) / torr (seawater pO2 - blood pO2) / minute at a given # temperature. # To convert to another unit: conv_resp_unit(value = 1e-6, from = "ml_O2 / mm2 / um / torr / min", to = "umol_O2 / cm2 / um / kPa / hr", temp = 20) # Now, with a knowledge of gill morphometrics, seawater pO2, and blood pO2, I can compare # gill diffusion with whole animal MO2.
Given the volume of the respirometer and the volume of bubbles or air space, the moles of O2 in the system are calculated, and the volume of a respirometer holding the same quantity of O2 with only water is returned.
correct_bubble(resp_vol, bubble_vol, temp = 25, sal = 35, atm_pres = 1013.25)
correct_bubble(resp_vol, bubble_vol, temp = 25, sal = 35, atm_pres = 1013.25)
resp_vol |
volume of the respirometer (liter). |
bubble_vol |
volume of the gas bubbles or headspace (mL). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
Depending on temperature and salinity, air holds 20,000x as much O2 as water per unit volume, thus small air bubbles in a respirometer can dramatically increase the amount of O2 an organism has to consume to lower the pO2 or aqueous [O2]. Thus air bubbles lead to underestimations of MO[2]. To correct for this in MO2 calculations after measurement, the volume of the respirometer can be increased. This function calculates the volume needed for MO2 calculations as a function of the volume of air space. Caution: allowing air bubbles into a respirometer is not recommended, even with this post-measurement adjustment. A small error in bubble volume estimation can lead to a large error in calculated metabolic rate.
The volume of a respirometer holding an equivalent quantity of O2 filled only with water.
Due to the high concentration of O2 in air, very small errors in bubble volume estimates can lead to very large differences in the volume returned. Only trust the returned value if you are very confident of the accuracy of your bubble volume estimate.
Matthew A. Birk, [email protected]
correct_bubble(resp_vol = 50, bubble_vol = 10) # a 10 mL bubble makes a huge difference! correct_bubble(resp_vol = 50, bubble_vol = 1, temp = 10, sal = 0) # in calculating MO2, a volume of 63.8 L should be used rather than the true 50 L.
correct_bubble(resp_vol = 50, bubble_vol = 10) # a 10 mL bubble makes a huge difference! correct_bubble(resp_vol = 50, bubble_vol = 1, temp = 10, sal = 0) # in calculating MO2, a volume of 63.8 L should be used rather than the true 50 L.
Given the seawater pH inside the respirometer and in the flush reservoir, the new carbonate parameters (including pH) in the respirometer after the flush are estimated.
flush_carb( resp_vol, flow_rate, duration, resp_pH, flush_pH, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
flush_carb( resp_vol, flow_rate, duration, resp_pH, flush_pH, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
resp_vol |
volume of the respirometer (liter). |
flow_rate |
rate of water flow into the respirometer (liters / minute). |
duration |
duration of the flush (minutes). |
resp_pH |
pH inside the respirometer before the flush (total scale). |
flush_pH |
pH of the water used for flushing the respirometer (total scale). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
A data frame returned by carb
.
Matthew A. Birk, [email protected]
flush_carb(resp_vol = 90, flow_rate = 10, duration = 3, resp_pH = 7.8, flush_pH = 8.1) # What will be the pH in the respirometer after this flush? flush_carb(resp_vol = 90, flow_rate = 10, duration = 3, resp_pH = 7.8, flush_pH = 8.1)$pH
flush_carb(resp_vol = 90, flow_rate = 10, duration = 3, resp_pH = 7.8, flush_pH = 8.1) # What will be the pH in the respirometer after this flush? flush_carb(resp_vol = 90, flow_rate = 10, duration = 3, resp_pH = 7.8, flush_pH = 8.1)$pH
Calculate the pO2 or [O2] in a respirometer after a flush. Given 5 of the 6 parameters, the 6th parameter is calculated.
flush_o2(resp_vol, flow_rate, duration, resp_o2, flush_o2, final_o2)
flush_o2(resp_vol, flow_rate, duration, resp_o2, flush_o2, final_o2)
resp_vol |
volume of the respirometer (liter). |
flow_rate |
rate of water flow into the respirometer (liters / minute). |
duration |
duration of the flush (minutes). |
resp_o2 |
O2 inside the respirometer before the flush (units do not matter as long as it is consistant with |
flush_o2 |
O2 of the water used for flushing the respirometer (units do not matter as long as it is consistant with |
final_o2 |
O2 of the water in the respirometer at the end of the flush (units do not matter as long as it is consistant with |
Matthew A. Birk, [email protected]
# What will be the pO2 in the respirometer after this flush? flush_o2(resp_vol = 90, flow_rate = 10, duration = 3, resp_o2 = 15, flush_o2 = 21) # I want to bring the pO2 back up to 95% air saturation. How long do I need to flush? flush_o2(resp_vol = 20, flow_rate = 2, resp_o2 = 75, flush_o2 = 99, final_o2 = 95)
# What will be the pO2 in the respirometer after this flush? flush_o2(resp_vol = 90, flow_rate = 10, duration = 3, resp_o2 = 15, flush_o2 = 21) # I want to bring the pO2 back up to 95% air saturation. How long do I need to flush? flush_o2(resp_vol = 20, flow_rate = 2, resp_o2 = 75, flush_o2 = 99, final_o2 = 95)
Calculate the proportion of water in a respirometer that is new after a flush. Useful for intermittent respirometry. Given 3 of the first 4 parameters, the 4th parameter is calculated.
flush_water(vol, flow_rate, duration, perc_fresh, plot = FALSE)
flush_water(vol, flow_rate, duration, perc_fresh, plot = FALSE)
vol |
volume of the respirometer (liter). |
flow_rate |
rate of water flow into the respirometer (liters / minute). |
duration |
duration of the flush (minutes). |
perc_fresh |
percent of the respirometer volume that is new flushed water. |
plot |
logical. Plot the percent exchanged as a function of flow rate and duration to see what effect would result if the rate or duration are changed. All parameters must only have a single value. |
Matthew A. Birk, [email protected]
Steffensen JF. 1989. Some errors in respirometry of aquatic breathers: How to avoid and correct for them. Fish Physiol Biochem. 6:49–59. Equation 5.
# What proportion of a 90 L respirometer is exchanged after 20 minutes of flow at 2 LPM? flush_water(vol = 90, flow_rate = 2, duration = 20) # Would it be worth it to extend the flush another five minutes? How much would that # improve the exchange? flush_water(vol = 90, flow_rate = 2, duration = 20, plot = TRUE) # Another five minutes would increase exchange by nearly 10%. # Perhaps that's worth the extra time... # Visualize flushing vol = 150 flow_rate = seq(0, 10, by = 0.5) duration = 0:60 perc_fresh = outer(flow_rate, duration, function(flow_rate, duration){ flush_water(vol = vol, flow_rate = flow_rate, duration = duration) }) persp(flow_rate, duration, perc_fresh, xlab = 'Flow rate (LPM)', ylab = 'Duration (min)', zlab = '% exchange', theta = 45, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)
# What proportion of a 90 L respirometer is exchanged after 20 minutes of flow at 2 LPM? flush_water(vol = 90, flow_rate = 2, duration = 20) # Would it be worth it to extend the flush another five minutes? How much would that # improve the exchange? flush_water(vol = 90, flow_rate = 2, duration = 20, plot = TRUE) # Another five minutes would increase exchange by nearly 10%. # Perhaps that's worth the extra time... # Visualize flushing vol = 150 flow_rate = seq(0, 10, by = 0.5) duration = 0:60 perc_fresh = outer(flow_rate, duration, function(flow_rate, duration){ flush_water(vol = vol, flow_rate = flow_rate, duration = duration) }) persp(flow_rate, duration, perc_fresh, xlab = 'Flow rate (LPM)', ylab = 'Duration (min)', zlab = '% exchange', theta = 45, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)
Calculates the pH of a flush reservoir that is needed to achieve the goal pCO2 after the flush reservoir has been drained into the respirometer.
goal_flush_pH( goal_pco2, resp_pH, resp_vol, flush_vol, flush_remain = 0, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
goal_flush_pH( goal_pco2, resp_pH, resp_vol, flush_vol, flush_remain = 0, temp = 25, sal = 35, TA = NULL, atm_pres = 1013.25 )
goal_pco2 |
the desired pCO2 in the respirometer after the flush (uatm). |
resp_pH |
pH inside the respirometer before the flush (total scale). |
resp_vol |
volume of the respirometer (liter). |
flush_vol |
volume of the flush reservoir (liter). |
flush_remain |
volume of the flush reservoir that will remain after the flush (liter). |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
pH needed in the flush reservoir to achieve the goal pCO2 post-flush (total scale).
Matthew A. Birk, [email protected]
co2_rate
, flush_carb
, carb
, peri_pump
# I want the respirometer to have a pCO2 = 1000 uatm. It currently has a pH of 7.6 and is 90 L. # If I have a 200 L reservoir which will be drained completely, what do I want # the pH of the reservoir to be? goal_flush_pH(goal_pco2 = 1000, resp_pH = 7.6, resp_vol = 90, flush_vol = 200)
# I want the respirometer to have a pCO2 = 1000 uatm. It currently has a pH of 7.6 and is 90 L. # If I have a 200 L reservoir which will be drained completely, what do I want # the pH of the reservoir to be? goal_flush_pH(goal_pco2 = 1000, resp_pH = 7.6, resp_vol = 90, flush_vol = 200)
Estimate total alkalinity from salinity and temperature of surface seawater according to Lee et al. 2006. Useful when a rough guess of TA is needed because measuring TA is not possible or practical.
guess_TA(temp = 25, sal = 35, region = NULL, extend = TRUE)
guess_TA(temp = 25, sal = 35, region = NULL, extend = TRUE)
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. 31 |
region |
(optional) geographic region. Options are "(Sub)tropics", "Equatorial Upwelling Pacific", "North Atlantic", "North Pacific", and "Southern Ocean". Default is |
extend |
logical. If salinity is |
temp
20 and 31
sal
38
temp
18 and 31
sal
36.5
0
temp
20 and 31
sal
37
temp
20 and 31
sal
35
temp
20 and 33
sal
36
Estimates total alkalinity using the equations provided by Lee et al. 2006 (Geophysical Research Letters). While these equations are designed for open ocean environments, they can provide a rough estimate even for coastal environments. For improved estimate accuracy, the geographic region can be provided. The North Pacific region is longitude-dependent so a longitude of 150 °W is assumed which provides a typical value within the range. Only applicable for surface waters, not very accurate for the ocean interior.
An estimate of the total alkalinity (umol / kg). If NA
or NaN
are returned, confirm the temp
and sal
values are within acceptable ranges for the region of interest.
Matthew A. Birk, [email protected]
Lee K, Tong LT, Millero FJ, Sabine CL, Dickson AG, Goyet C, Park G-H, Wanninkhof R, Feely RA, Key RM. 2006. Global relationships of total alkalinity with salinity and temperature in surface waters of the world’s oceans. Geophys Res Lett. 33:L19605.
guess_TA(temp = 22, sal = 33) guess_TA(temp = 12, sal = 33, region = "North Atlantic") guess_TA(temp = 20, sal = 31:35) guess_TA(sal = 31) # salinity is within bounds guess_TA(sal = 30) # salinity is outside the bounds and TA is extrapolated guess_TA(sal = 30, extend = FALSE) # do not extrapolate TA guess_TA(sal = 25, extend = TRUE) # will not extrapolate with sal > 5 psu out of bounds
guess_TA(temp = 22, sal = 33) guess_TA(temp = 12, sal = 33, region = "North Atlantic") guess_TA(temp = 20, sal = 31:35) guess_TA(sal = 31) # salinity is within bounds guess_TA(sal = 30) # salinity is outside the bounds and TA is extrapolated guess_TA(sal = 30, extend = FALSE) # do not extrapolate TA guess_TA(sal = 25, extend = TRUE) # will not extrapolate with sal > 5 psu out of bounds
Estimates the time at which O2 will reach a defined level assuming a linear change in O2 over time.
guess_when(past_o2, past_time, goal_o2, plot = TRUE)
guess_when(past_o2, past_time, goal_o2, plot = TRUE)
past_o2 |
a numeric vector of at least two oxygen measurements previously during the trial. |
past_time |
a vector of timepoints corresponding to when |
goal_o2 |
a numeric vector or single value describing the O2 level of interest. |
plot |
logical. Do you want to see a plot to visualize this prediction? |
A prediction of the time when O2 will reach goal_o2
. If past_time
is numeric, then a numeric value(s) will be returned. If POSIX, then POSIX will be returned.
Viewing the plot can be valuable if the O2 consumption or production is not linear.
Matthew A. Birk, [email protected]
guess_when(past_o2 = rnorm(n = 10, mean = 100:91), past_time = 1:10, goal_o2 = 75, plot = FALSE) guess_when(past_o2 = rnorm(n = 10, mean = 100:91, sd = 5), past_time = 1:10, goal_o2 = 75) # Viewing the plot can be helpful to see how trustworthy the prediction is # when signal:noise is low.
guess_when(past_o2 = rnorm(n = 10, mean = 100:91), past_time = 1:10, goal_o2 = 75, plot = FALSE) guess_when(past_o2 = rnorm(n = 10, mean = 100:91, sd = 5), past_time = 1:10, goal_o2 = 75) # Viewing the plot can be helpful to see how trustworthy the prediction is # when signal:noise is low.
Imports the standard txt file output from Pyroscience's deprecated Pyro Oxygen Logger software and converts the data into one or more data frames. If using the newer Pyroscience Workbench software, use import_pyroscience_workbench
instead.
import_firesting( file, o2_unit = "percent_a.s.", date = "%m/%d/%Y %X", overwrite_sal = NULL, keep_metadata = FALSE, drop_channels = TRUE, split_channels = FALSE )
import_firesting( file, o2_unit = "percent_a.s.", date = "%m/%d/%Y %X", overwrite_sal = NULL, keep_metadata = FALSE, drop_channels = TRUE, split_channels = FALSE )
file |
a character string. The filepath for the file to be read. |
o2_unit |
a character string. The unit of O2 measurement to be output in the data frame. Options are described in |
date |
a character string. The date format to be passed to |
overwrite_sal |
Default |
keep_metadata |
logical. Should metadata from the file be returned as extra columns in the returned data frame? Default is |
drop_channels |
logical. Should channels without any O2 data be dropped? Default is |
split_channels |
logical. Should a list of data frames be returned with a separate data frame for each channel? Default is |
The following FireSting fiber optic O2 transmitters are supported:
FireStingO2
FireStingO2 (1st generation)
If you would like support for the Piccolo2, FireStingO2-Mini, TeX4, or any OEM instruments, email me a data file from the device.
A data frame (or list of data frames) is returned.
Date and time, POSIXlt format.
Duration of measurement trial (minutes).
Oxygen measurement in desired unit as determined by o2_unit
.
Temperature recorded or defined at beginning of measurement trial.
Salinity (psu).
Channel columns (CH_...) are repeated for each channel.
Comments from FireSting file.
If keep_metadata = TRUE
, then the following columns are appended to the returned data frame:
Atmospheric pressure (mbar).
Relative humidity (% RH).
Probe temperature.
Transmitter internal temperature.
Voltage input from the extension port (mV).
Phase recorded. Phase is inversely related to O2.
Intensity is an indicator of the quality of the signal. A low intensity warning is produced by the transmitter below 10 mV.
Ambient light on the sensor. Expressed in mV.
If split_channels = TRUE
, then "CH_X_
" is removed from the column names and multiple data frames are returned in a named list.
Oxygen conversions are estimates based on the marelac
package.
Matthew A. Birk, [email protected]
import_pyroscience_workbench
, import_presens
, import_witrox
, conv_o2
## Not run: file <- system.file('extdata', 'pyro_oxygen_logger_file.txt', package = 'respirometry') import_firesting(file, o2_unit = 'umol_per_l') # I want each channel as a separate data frame. data_list <- import_firesting(file, split_channels = TRUE) data_list$CH_3 # here's the channel 3 data frame. ## End(Not run)
## Not run: file <- system.file('extdata', 'pyro_oxygen_logger_file.txt', package = 'respirometry') import_firesting(file, o2_unit = 'umol_per_l') # I want each channel as a separate data frame. data_list <- import_firesting(file, split_channels = TRUE) data_list$CH_3 # here's the channel 3 data frame. ## End(Not run)
Imports the standard text file output from most single channel PreSens fiber optic O2 transmitters and converts the data into a data frame.
import_presens( file, o2_unit = "percent_a.s.", date = "%d/%m/%y", sal = 35, all_cols = FALSE, split_channels = FALSE )
import_presens( file, o2_unit = "percent_a.s.", date = "%d/%m/%y", sal = 35, all_cols = FALSE, split_channels = FALSE )
file |
a character string. The filepath for the file to be read. |
o2_unit |
a character string. The unit of O2 measurement to be output in the data frame. Options are described in |
date |
a character string. The date format to be passed to |
sal |
salinity of water sample (psu). Default is 35 psu. Ignored for Fibox 4 files since salinity is provided by the file. |
all_cols |
logical. For Fibox 4 files only. Should all columns (including calibration data and serial numbers) be output? |
split_channels |
logical. For SDR SensorDish only. Should a list of data frames be returned with a separate data frame for each channel? Default is |
The following PreSens fiber optic O2 transmitters are supported:
Fibox 4
Fibox 3
Fibox 3 trace
Fibox 3 LCD trace
Microx TX3
Microx TX3 trace
SDR SensorDish Reader
If you would like support for another PreSens O2 meter, email the package maintainer a data file from the device you would like supported.
It is very important to note that the PreSens fiber optics O2 transmitters that are supported with this function (except the Fibox 4) DO NOT account for salinity (i.e. they assume salinity = 0 ppt). If the water sample measured was not fresh water, the oxygen concentrations (e.g. mg per liter or umol per liter) are incorrect in the PreSens txt file. This function corrects these O2 concentrations based on the salinity value defined by the sal
argument. Absolute partial pressures (i.e. hPa and torr) will also be slightly different due to the slight influence of salinity on water's vapor pressure. This difference is typically ~0.05% of the recorded value.
A data frame is returned.
Date and time, POSIXct format.
Duration of measurement trial (minutes).
Oxygen measurement in desired unit as determined by o2_unit
.
Phase recorded. Phase is inversely related to O2. Not included in SDR SensorDish Reader files.
Amplitude recorded. Amplitude is an indicator of the quality of the signal. A low amplitude warning is produced by the transmitter below 2500. Not included in SDR SensorDish Reader files.
Temperature recorded or defined at beginning of measurement trial.
Atmospheric pressure (mbar).
Salinity (psu).
Error code from transmitter. See PreSens user manual for translation of error code. Not included in SDR SensorDish Reader files.
Oxygen conversions are based on conv_o2
and therefore differ slightly from the conversions provided by PreSens.
Matthew A. Birk, [email protected]
import_pyroscience_workbench
, import_firesting
, import_witrox
, conv_o2
## Not run: # Import a Fibox 3 file. file <- system.file('extdata', 'fibox_3_file.txt', package = 'respirometry') import_presens(file, o2_unit = 'umol_per_l', sal = 25) # Import a Fibox 4 file. file <- system.file('extdata', 'fibox_4_file.csv', package = 'respirometry') import_presens(file = file, date = '%d-%b-%Y') # Import an SDR SensorDish Reader file. file <- system.file('extdata', 'sdr_file.txt', package = 'respirometry') import_presens(file = file, date = '%d.%m.%y%X') ## End(Not run)
## Not run: # Import a Fibox 3 file. file <- system.file('extdata', 'fibox_3_file.txt', package = 'respirometry') import_presens(file, o2_unit = 'umol_per_l', sal = 25) # Import a Fibox 4 file. file <- system.file('extdata', 'fibox_4_file.csv', package = 'respirometry') import_presens(file = file, date = '%d-%b-%Y') # Import an SDR SensorDish Reader file. file <- system.file('extdata', 'sdr_file.txt', package = 'respirometry') import_presens(file = file, date = '%d.%m.%y%X') ## End(Not run)
Imports the raw channel data from Pyroscience Workbench output files. This allows "live" analyses while the trial is still running. This does not utilize the ".pyr" file, nor the text file that is created once the trial is finished. This utilizes the raw channel data found within the "ChannelData" folder that the software makes when the trial starts.
import_pyroscience_workbench( folder, o2_unit = "percent_a.s.", sal = NULL, keep_metadata = FALSE, split_channels = FALSE, merge_close_measurements = "min" )
import_pyroscience_workbench( folder, o2_unit = "percent_a.s.", sal = NULL, keep_metadata = FALSE, split_channels = FALSE, merge_close_measurements = "min" )
folder |
a character string. The filepath to the parent folder (directory) which contains ChannelData. |
o2_unit |
a character string. The unit of O2 measurement to be output in the data frame. Options are described in |
sal |
numeric. If |
keep_metadata |
logical. Should metadata from the file be returned as extra columns in the returned data frame? Default is |
split_channels |
logical. Should a list of data frames be returned with a separate data frame for each channel? Default is |
merge_close_measurements |
used only when
|
A data frame (or list of data frames) is returned.
Date and time, POSIXct format. If split_channels = FALSE
(default), then the timestamp is the average of all the measurements that were merged. For details, see merge_close_measurements
.
Duration of measurement trial (minutes).
Oxygen measurement in desired unit as determined by o2_unit
.
Temperature recorded or defined at beginning of measurement trial.
Salinity (psu). Only displayed if sal != NULL
.
Warning or error messages from Pyroscience Workbench file.
Channel columns (CH_...) are repeated for each channel.
If keep_metadata = TRUE
, then the following columns are appended to the returned data frame:
Phase recorded. Phase is inversely related to O2.
Intensity is an indicator of the quality of the signal.
Ambient light on the sensor. Expressed in mV.
Warning or error messages from Pyroscience Workbench file's temperature measurement.
Atmospheric pressure (mbar).
Warning or error messages from Pyroscience Workbench file's atmospheric pressure measurement.
If split_channels = TRUE
, then "CH_X_
" is removed from the column names and multiple data frames are returned in a named list.
Oxygen conversions are estimates based on the marelac
package.
Matthew A. Birk, [email protected]
import_presens
, import_witrox
, conv_o2
## Not run: folder <- system.file('extdata/pyro_wb/', package = 'respirometry') import_pyroscience_workbench(folder = folder, o2_unit = 'umol_per_l', sal = c(0, 35)) # I want each channel as a separate data frame. data_list <- import_pyroscience_workbench(folder = folder, split_channels = TRUE) data_list$CH_2 # here's the channel 2 data frame. ## End(Not run)
## Not run: folder <- system.file('extdata/pyro_wb/', package = 'respirometry') import_pyroscience_workbench(folder = folder, o2_unit = 'umol_per_l', sal = c(0, 35)) # I want each channel as a separate data frame. data_list <- import_pyroscience_workbench(folder = folder, split_channels = TRUE) data_list$CH_2 # here's the channel 2 data frame. ## End(Not run)
Imports the standard txt file output from Loligo Systems Witrox fiber optic O2 transmitters and converts the data into one or more data frames.
import_witrox( file, o2_unit = "percent_a.s.", date = "%m/%d/%Y %I:%M:%S %p", overwrite_sal = NULL, drop_channels = TRUE, split_channels = FALSE )
import_witrox( file, o2_unit = "percent_a.s.", date = "%m/%d/%Y %I:%M:%S %p", overwrite_sal = NULL, drop_channels = TRUE, split_channels = FALSE )
file |
a character string. The filepath for the file to be read. |
o2_unit |
a character string. The unit of O2 measurement to be output in the data frame. Options are described in |
date |
a character string. The date format to be passed to |
overwrite_sal |
Default |
drop_channels |
logical. Should channels without any O2 data be dropped? Default is |
split_channels |
logical. Should a list of data frames be returned with a separate data frame for each channel? Default is |
The following Loligo Systems fiber optic O2 transmitters are supported:
Witrox 4
If you would like support for the Witrox 1, email me a data file from this device.
A data frame (or list of data frames) is returned.
Date and time, POSIXlt format.
Duration of measurement trial (minutes).
Atmospheric pressure (mbar).
Phase recorded. Phase is inversely related to O2.
Temperature recorded or defined at beginning of measurement trial.
Salinity (psu).
Oxygen measurement in desired unit as determined by o2_unit
.
Channel columns (CH_...) are repeated for each channel.
If split_channels = TRUE
, then "CH_X_
" is removed from the column names and multiple data frames are returned in a list.
Matthew A. Birk, [email protected]
import_pyroscience_workbench
, import_firesting
, import_presens
, conv_o2
## Not run: file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') import_witrox(file, o2_unit = 'umol_per_l') # Oops. I forgot to change the salinity value when I calibrated # the instrument. Override the values in the file for 35 psu. import_witrox(file, o2_unit = 'umol_per_kg', overwrite_sal = 35) # I want each channel as a separate data frame. data_list <- import_witrox(file, split_channels = TRUE) data_list$CH_3 # here's the channel 3 data frame. ## End(Not run)
## Not run: file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') import_witrox(file, o2_unit = 'umol_per_l') # Oops. I forgot to change the salinity value when I calibrated # the instrument. Override the values in the file for 35 psu. import_witrox(file, o2_unit = 'umol_per_kg', overwrite_sal = 35) # I want each channel as a separate data frame. data_list <- import_witrox(file, split_channels = TRUE) data_list$CH_3 # here's the channel 3 data frame. ## End(Not run)
The width of time bins seems to be an under-appreciated consideration when calculating metabolic rates if PO2 or time are interesting covariates. The wider the bins, the higher the precision of your calculated MO2 value (more observations to average over), but at a loss of resolution of an interesting covariate. The narrower the bins, the higher the resolution of the PO2 or time covariate, but at a cost of lower precision. For Pcrit trials, I have found good success using bins of 1/10th the trial duration at the highest PO2s (where good precision is important) and 1/100th the trial duration at the lowest PO2s (where good resolution is important). Thus, these are the defaults, but can be changed as desired.
make_bins( o2, duration, good_data = TRUE, min_o2_width = 1/100, max_o2_width = 1/10, n_thresholds = 10 )
make_bins( o2, duration, good_data = TRUE, min_o2_width = 1/100, max_o2_width = 1/10, n_thresholds = 10 )
o2 |
numeric vector of O2 observations. |
duration |
numeric vector of the timepoints for each observation (minutes). |
good_data |
logical vector of whether O2 observations are "good" measurements and should be included in analysis. Default is that all observations are |
min_o2_width |
The duration of the bins at the lowest O2 value, expressed as a proportion of the total "good" trial duration. Default is 1/100th of the total "good" trial duration. |
max_o2_width |
The duration of the bins at the highest O2 value, expressed as a proportion of the total "good" trial duration. Default is 1/10th of the total "good" trial duration. |
n_thresholds |
Default is 10. |
A data.frame with n_thresholds
rows and two columns is returned. Each row describes the threshold and the duration of observations that will be binned together at or above the corresponding O2 value.
The various O2 thresholds at which bin widths change.
The bin width applied to values greater than the corresponding row's O2 value but less than the next greater O2 value.
Matthew A. Birk, [email protected]
# get O2 data file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') o2_data <- na.omit(import_witrox(file, split_channels = TRUE)$CH_4) # Total trial duration is 21.783 minutes make_bins(o2 = o2_data$O2, duration = o2_data$DURATION) # creates the default 10 bins. At the # highest O2 levels, bin widths are 21.783/10 = 2.1783 mins and at the lowest O2 levels, bin # widths are 21.783/100 = 0.21783 mins. bins <- make_bins(o2 = o2_data$O2, duration = o2_data$DURATION, min_o2_width = 1/20, max_o2_width = 1/3, n_thresholds = 5) # creates 5 bins. At the highest O2 levels, bin widths are # 21.783/3 = 7.261 mins and at the lowest O2 levels, bin widths are 21.783/20 = 1.089 mins. (mo2 <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = bins, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL))
# get O2 data file <- system.file('extdata', 'witrox_file.txt', package = 'respirometry') o2_data <- na.omit(import_witrox(file, split_channels = TRUE)$CH_4) # Total trial duration is 21.783 minutes make_bins(o2 = o2_data$O2, duration = o2_data$DURATION) # creates the default 10 bins. At the # highest O2 levels, bin widths are 21.783/10 = 2.1783 mins and at the lowest O2 levels, bin # widths are 21.783/100 = 0.21783 mins. bins <- make_bins(o2 = o2_data$O2, duration = o2_data$DURATION, min_o2_width = 1/20, max_o2_width = 1/3, n_thresholds = 5) # creates 5 bins. At the highest O2 levels, bin widths are # 21.783/3 = 7.261 mins and at the lowest O2 levels, bin widths are 21.783/20 = 1.089 mins. (mo2 <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$O2, bin_width = bins, vol = 10, temp = o2_data$TEMP, sal = o2_data$SAL))
Calculates the maximum oxygen consumption rate (MO2) supported by a respirometer with a given flow rate. Useful for ensuring an acclimating animal maintains a normoxic environment.
max_MO2( flow_rate, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 ) max_mo2( flow_rate, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 )
max_MO2( flow_rate, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 ) max_mo2( flow_rate, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 )
flow_rate |
water flow rate into respirometer (liters / min). |
min_pO2 |
minimum pO2 acceptable in respirometer (% air saturation). Default is 90% air saturation. |
pO2_in |
pO2 of water entering respirometer (% air saturation). Default is 100% air saturation. |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
The maximum whole-animal oxygen consumption rate (umol / hr) that can be sustained.
Keep in mind that most organisms are very stressed upon being placed in a respirometer and their MO2 may be much higher than basal MO2.
Matthew A. Birk, [email protected]
Steffensen JF. 1989. Some errors in respirometry of aquatic breathers: How to avoid and correct for them. Fish Physiol Biochem. 6:49–59. Equation 8.
max_MO2(flow_rate = 1) # What is the maximum MO2 organism I can place in my respirometer and still maintain at # least 75% air saturation when the intake fresh water is 1.5 LPM, 10 °C and 90% air saturated? (max_mo2 <- max_MO2(flow_rate = 1.5, min_pO2 = 75, pO2_in = 90, temp = 10, sal = 0)) # If a 300 g individual has an MO2 of 2000 umol/hr, how big of an animal can I use? scale_MO2(mass_1 = 300, MO2_1 = 2000, MO2_2 = max_mo2) # I can almost support a 1 kg individual!
max_MO2(flow_rate = 1) # What is the maximum MO2 organism I can place in my respirometer and still maintain at # least 75% air saturation when the intake fresh water is 1.5 LPM, 10 °C and 90% air saturated? (max_mo2 <- max_MO2(flow_rate = 1.5, min_pO2 = 75, pO2_in = 90, temp = 10, sal = 0)) # If a 300 g individual has an MO2 of 2000 umol/hr, how big of an animal can I use? scale_MO2(mass_1 = 300, MO2_1 = 2000, MO2_2 = max_mo2) # I can almost support a 1 kg individual!
Calculates mean pH from a vector of pH values by averaging [H+] rather than numerical pH values.
mean_pH(pH, na.rm = FALSE, ...)
mean_pH(pH, na.rm = FALSE, ...)
pH |
a numeric vector of pH values. |
na.rm |
a logical value indicating whether NA values should be stripped before the computation proceeds. |
... |
further arguments passed to or from other methods. |
Since pH is on a logarithmic scale, averaging pH values directly does not provide the true arithmetic mean of what is likely truly important to the organism, [H+] (however, see Boutilier and Shelton 1980). Thus, the pH values are converted to [H+] then averaged and converted back to a mean pH value.
Matthew A. Birk, [email protected]
Boutilier RG, Shelton G. 1980. The statistical treatment of hydrogen ion concentration and pH. J Exp Biol. 84:335–339.
mean_pH(c(7, 8)) # 7.26 rather than 7.5!
mean_pH(c(7, 8)) # 7.26 rather than 7.5!
Calculates the minimum flow rate into a respirometer required to maintain a high pO2. Useful for ensuring an acclimating animal maintains a normoxic environment. It can also be used to estimate the flow rate needed for a given pO2 decrease desired for flow-through respirometry.
min_flow( MO2, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 )
min_flow( MO2, min_pO2 = 90, pO2_in = 100, temp = 25, sal = 35, atm_pres = 1013.25 )
MO2 |
whole-animal oxygen consumption rate (umol / hour). |
min_pO2 |
minimum pO2 acceptable in respirometer (% air saturation). Default is 90% air saturation. |
pO2_in |
pO2 of water entering respirometer (% air saturation). Default is 100% air saturation. |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
The flow rate (liters / min) into the respirometer required for the steady state pO2 to be min_pO2
.
Keep in mind that most organisms are very stressed upon being placed in a respirometer and their MO2 may be much higher than basal MO2.
Matthew A. Birk, [email protected]
Steffensen JF. 1989. Some errors in respirometry of aquatic breathers: How to avoid and correct for them. Fish Physiol Biochem. 6:49–59. Equation 8.
min_flow(MO2 = 1000) # What is the minimum flow rate required to maintain at least 75% air saturation in a # respirometer with an organism(s) with an oxygen consumption rate of 1000 umol/h # when the intake fresh water is 10 °C and 90% air saturated? min_flow(MO2 = 1000, min_pO2 = 75, pO2_in = 90, temp = 10, sal = 0)
min_flow(MO2 = 1000) # What is the minimum flow rate required to maintain at least 75% air saturation in a # respirometer with an organism(s) with an oxygen consumption rate of 1000 umol/h # when the intake fresh water is 10 °C and 90% air saturated? min_flow(MO2 = 1000, min_pO2 = 75, pO2_in = 90, temp = 10, sal = 0)
Given the number of moles of a gas, calculates the liters to run through a peristaltic pump.
peri_pump( mol, species = "O2", temp = 25, reg_pres, reg_unit = "psi", atm_pres = 1013.25 )
peri_pump( mol, species = "O2", temp = 25, reg_pres, reg_unit = "psi", atm_pres = 1013.25 )
mol |
number of moles to go through a peristaltic pump. |
species |
character string describing the gas species. Options are available from |
temp |
temperature (°C). Default is 25 °C. |
reg_pres |
gauge pressure from the gas regulator into the peristaltic pump. |
reg_unit |
unit used in |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
Most mass flow controllers are programmed with a "standard condition" something like 0 *C and 1013 mbar for which they account for the pressure and temperature of an incoming gas source. For setups without expensive mass flow controllers, a more affordable alternative is to use a peristaltic pump. These do not account for variations in incoming gas pressure and temperature and thus, it must be calculated to set the peristaltic pump to the correct RPM.
Matthew A. Birk, [email protected]
peri_pump(mol = 0.5, species = 'O2', temp = 10, reg_pres = 5, reg_unit = "kPa") # To flow 0.5 moles of O2, then flow 11.1 L.
peri_pump(mol = 0.5, species = 'O2', temp = 10, reg_pres = 5, reg_unit = "kPa") # To flow 0.5 moles of O2, then flow 11.1 L.
Creates a Pcrit plot (the threshold below which oxygen consumption rate can no longer be sustained) based on paired PO2 and MO2 values. Five Pcrit metrics are plotted: the traditional breakpoint metric (broken stick regression, black), the nonlinear regression metric (Marshall et al. 2013, green), the sub-prediction interval metric (Birk et al. 2019, red), the alpha-based Pcrit method (Seibel et al., 2021, blue), and the linear low O2 (LLO) method (Reemeyer & Rees 2019, purple). For details on how the Pcrit values are calculated, see calc_pcrit
.
plot_pcrit( po2, mo2, mo2_data, method = "Breakpoint", avg_top_n = 1, level = 0.95, iqr = 1.5, NLR_m = 0.065, MR = NULL, mo2_threshold = Inf, showNLRs = FALSE, ... )
plot_pcrit( po2, mo2, mo2_data, method = "Breakpoint", avg_top_n = 1, level = 0.95, iqr = 1.5, NLR_m = 0.065, MR = NULL, mo2_threshold = Inf, showNLRs = FALSE, ... )
po2 |
a vector of PO2 values. Any unit of measurement should work, but the NLR calculation was optimized using kPa. If the NLR metric is giving you trouble, try converting to kPa using |
mo2 |
a vector of metabolic rate values. Must be the same length and corresponding to |
mo2_data |
for convenience, the output of |
method |
Over the years, many different methods of analysis have been proposed to quantify Pcrit. You must choose one of the following: Alpha, Breakpoint (default), LLO, NLR, Sub_PI, All. If in doubt, try "All". |
avg_top_n |
applies to the |
level |
applies to the |
iqr |
applies to the |
NLR_m |
applies to the |
MR |
applies to the |
mo2_threshold |
applies to the |
showNLRs |
logical. Should all the NLR functions be plotted in a second plot? If |
... |
arguments to be passed to |
Alpha is calculated from calc_alpha
and the Pcrit corresponding to MR
is returned. This determine's the animal's oxygen supply capacity and calculates the Pcrit at any given metabolic rate of interest. If no MR
is provided, then it defaults to the mean MO2 value from the oxyregulating portion of the curve (as defined by the broken-stick regression).
Data are fit to a broken-stick regression using segmented
.
A subset of observations are chosen only from those with an MO2 < MR
. Then, a linear model is fit through the observations and Pcrit is calculated as the PO2 at which the line reaches MR
.
Data are fit to the following functions: Michaelis-Menten, Power, Hyperbola, Pareto, and Weibull with intercept. Following the method developed by Marshall et al. 2013, the function that best fits the data (smallest AIC) is chosen and the Pcrit is determined as the PO2 at which the slope of the function is NLR_m
(by default = 0.065 following the authors' suggestion).
This metric builds off the Breakpoint
metric and results in a systematically lower Pcrit value. This is useful for applications where it is important to ensure that Pcrit is not being overestimated. It represents a reasonable lower bounded estimate of the Pcrit value for a given trial. Once the Breakpoint
Pcrit is calculated, a 95% prediction interval (can be changed with the level
argument) is calculated around the oxyregulating region (i.e. using PO2 values > breakpoint Pcrit). By default, iqr
provides some filtering of abberant observations to prevent their influence on the calculated prediction interval. Finally, the Sub_PI Pcrit value is returned at the intersection of the oxyconforming line and the lower limit of the oxyregulating prediction interval.
A plot is created showing the relationship between PO2 and MO2. Based on the method
used, the alpha, breakpoint, LLO, NLR, and/or sub-PI Pcrit values are shown in the title and on the plot by inverted triangles.
For breakpoint and sub-PI methods, the broken-stick regression is shown by black lines. The gray bands represent the confidence interval (defaults to 95% but will change with level
).
For the sub-PI method, the dashed red curves signify the prediction interval used. Black circles represent oxyregulating observations used in the generation of the prediction interval, while grey circles represent both the oxyconforming observations and those observations outside the IQR threshold (defined by iqr
).
For the NLR method, the green curve represents the best fitting NLR function and the green inverted triangle represents the NLR Pcrit (modified by NLR_m
)
For the Alpha method, the blue line represents alpha, which was fit based on the blue circle observation(s). If MR
is not defined by the user, then the black points are those that were averaged to choose MR
. These are the "oxyregulating" observations based on the breakpoint method.
If showNLRs = TRUE
, then a second plot is generated which shows all the NLR functions that converged. Vertical lines represent the Pcrit values corresponding to each curve.
Black = Michaelis-Menten
Red = Power
Green = Hyperbola
Blue = Pareto
Cyan = Weibull with intercept.
Matthew A. Birk, [email protected]
Birk, Matthew A., K.A.S. Mislan, Karen F. Wishner, and Brad A. Seibel. 2019. “Metabolic Adaptations of the Pelagic Octopod Japetella Diaphana to Oxygen Minimum Zones.” Deep-Sea Research Part I 148: 123–31.
Marshall, Dustin J., Michael Bode, and Craig R. White. 2013. “Estimating Physiological Tolerances - a Comparison of Traditional Approaches to Nonlinear Regression Techniques.” Journal of Experimental Biology 216(12): 2176–82.
Reemeyer, Jessica E., and Bernard B. Rees. 2019. “Standardizing the Determination and Interpretation of Pcrit in Fishes.” Journal of Experimental Biology 222(18): jeb210633.
Seibel, B. A., A. Andres, M. A. Birk, A. L. Burns, C. T. Shaw, A. W. Timpe, C. J. Welsh. 2021. “Oxygen supply capacity breathes new life into the critical oxygen partial pressure (Pcrit).” Journal of Experimental Biology.
raw_data <- system.file('extdata/pcrit_run/', package = 'respirometry') o2_data <- import_pyroscience_workbench(folder = raw_data) mo2_data <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$CH_1_O2, bin_width = 10, vol = 3) plot_pcrit(mo2_data = mo2_data) par(mfrow = c(2, 1)) plot_pcrit(po2 = mo2_data$O2_MEAN, mo2 = mo2_data$MO2, method = 'All', MR = 100, showNLRs = TRUE)
raw_data <- system.file('extdata/pcrit_run/', package = 'respirometry') o2_data <- import_pyroscience_workbench(folder = raw_data) mo2_data <- calc_MO2(duration = o2_data$DURATION, o2 = o2_data$CH_1_O2, bin_width = 10, vol = 3) plot_pcrit(mo2_data = mo2_data) par(mfrow = c(2, 1)) plot_pcrit(po2 = mo2_data$O2_MEAN, mo2 = mo2_data$MO2, method = 'All', MR = 100, showNLRs = TRUE)
Predicts the [NH3] and [NH4+] of seawater after a defined amount of oxygen consumption. Ammonotelic animals excrete the ionized form NH4+ (ammonium) but some of these ions dissociate into unionized NH3 (ammonia) which is toxic for most fishes and crustaceans around 0.4-2.0 mg/L (Boyd 2012).
predict_nh3( o2_drop = 10, o2_unit = "percent_a.s.", o2_nh4_ratio, temp = 25, sal = 35, pH = 8.1, atm_pres = 1013.25 )
predict_nh3( o2_drop = 10, o2_unit = "percent_a.s.", o2_nh4_ratio, temp = 25, sal = 35, pH = 8.1, atm_pres = 1013.25 )
o2_drop |
a numeric value or vector describing the change in O2. Default is 10. |
o2_unit |
a string describing the unit used to measure |
o2_nh4_ratio |
molar ratio of O2 consumed to NH4+ produced. |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
pH |
seawater pH (total scale). Default is 8.1. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
Given a known amount of oxygen consumed and an estimated O2:N ratio, the amount of NH4 produced can be estimated. Production or consumption of ammonium by "background" microbes or conversion of ammonium to nitrite and nitrate is ignored since bacteria in the respirometer are typically sought to be in low levels. The amount of dissociation to produce ammonia is calculated by Kn
.
A list containing the predicted NH3, NH4+, and TAN produced in mg/l.
Matthew A. Birk, [email protected]
Boyd C. 2012. Water Quality. In "Aquaculture: Farming Aquatic Animals and Plants". Blackwell Publishing, Ltd.
predict_nh3(o2_drop = 25, o2_nh4_ratio = 10)
predict_nh3(o2_drop = 25, o2_nh4_ratio = 10)
Predicts the pH of seawater after a defined amount of oxygen consumption.
predict_pH( start_o2 = 100, end_o2, start_pH, temp = 25, sal = 35, RQ = 1, TA = NULL, all_carb = FALSE )
predict_pH( start_o2 = 100, end_o2, start_pH, temp = 25, sal = 35, RQ = 1, TA = NULL, all_carb = FALSE )
start_o2 |
pO2 at the start of the measurement (% air saturation). Default is 100% air saturation. |
end_o2 |
pO2 at the end of the measurment (% air saturation). |
start_pH |
seawater pH (total scale) at the start of the measurement. |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. If |
RQ |
respiratory quotient: ratio of CO2 produced / O2 consumed. Default is 1. |
TA |
(optional) total alkalinity (umol / kg). If undefined TA is estimated from salinity using |
all_carb |
logical. Should all carbonate chemistry parameters be returned? Default is FALSE. |
Given a known amount of oxygen consumed and an estimated respiratory quotient (see Q10
), the amount of CO2 produced can be estimated. From this CO2 production estimate, the carbonate chemistry of the seawater can be estimated. Atmospheric pressure is assumed.
If all_carb
is FALSE
, then a list of the predicted pH (total scale) at the end of the measurement and the predicted pCO2 (uatm) are returned. If all_carb
is TRUE
, then the predicted carbonate chemistry parameters are returned from carb
.
Matthew A. Birk, [email protected]
predict_pH(end_o2 = 75, start_pH = 8.1) predict_pH(start_o2 = 75, end_o2 = 50, start_pH = 7.96, temp = 15, sal = 33, RQ = 0.88) # I know pH at the end was 7.8, but what was pH at the beginning? predict_pH(start_o2 = 75, end_o2 = 100, start_pH = 8.013536) # reverse the order
predict_pH(end_o2 = 75, start_pH = 8.1) predict_pH(start_o2 = 75, end_o2 = 50, start_pH = 7.96, temp = 15, sal = 33, RQ = 0.88) # I know pH at the end was 7.8, but what was pH at the beginning? predict_pH(start_o2 = 75, end_o2 = 100, start_pH = 8.013536) # reverse the order
Calculates parameters from Q10 temperature coefficient for chemical or biological systems. This function can be used in two ways. 1. if four of the first five parameters are given (Q10
, R1
, R2
, T1
, T2
) then the fifth parameter is returned, or 2. if R_vec
and T_vec
are given, then the best Q10 for those data is returned.
Q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE) q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE) calc_q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE)
Q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE) q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE) calc_q10(Q10, R1, R2, T1, T2, R_vec, T_vec, model = FALSE)
Q10 |
factor by which rate changes due to 10 °C increase in temperature. |
R1 |
rate 1. Could also be Pcrit or any other temperature-dependent biological parameters. |
R2 |
rate 2. Could also be Pcrit or any other temperature-dependent biological parameters. |
T1 |
temperature 1 (in °C). |
T2 |
temperature 2 (in °C). |
R_vec |
a vector of temperature-dependent values, such as rates (e.g. MO2), Pcrit or other biological parameters. |
T_vec |
a vector of temperature values (in °C). |
model |
logical. If |
Matthew A. Birk, [email protected]
scale_MO2
, calc_E
, adj_by_temp
Q10(R1 = 5, R2 = 10, T1 = 10, T2 = 20) # Returns Q10; = 2 Q10(Q10 = 2.66, R1 = 5, T1 = 10, T2 = 20) # Returns R2; = 13.3 # My species has an MO2 of 9.5 umol/g/h at 10 *C. What MO2 should I expect at 13 *C? Q10(Q10 = 2, R1 = 9.5, T1 = 10, T2 = 13) # expect ~11.7 umol/g/h at 13 *C. # I measured MO2 at a spectrum of temperatures. What Q10 value best fits my data? Q10(R_vec = c(1, 2, 5, NA, 18, 33), T_vec = c(0, 10, 20, 30, 40, 50)) # A 100 g individual at 10 *C has an MO2 of 1270 umol/h. How much # would a 250 g individual likely consume at 14 *C? Q10(Q10 = 2, R1 = scale_MO2(mass_1 = 100, MO2_1 = 1270, mass_2 = 250), T1 = 10, T2 = 14) # Visualize MO2 scaling by mass and temperature: mass <- seq(10, 200, 10) temp <- 10:25 base_mass <- 50 base_temp <- 20 base_MO2 <- 750 mo2 <- outer(mass, temp, function(mass, temp){ scale_MO2(mass_1 = base_mass, mass_2 = mass, MO2_1 = Q10(Q10 = 2, R1 = base_MO2, T1 = base_temp, T2 = temp)) }) persp(mass, temp, mo2, xlab = 'Mass (g)', ylab = 'Temperature (*C)', zlab = 'MO2 (umol / hr)', theta = 35, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)
Q10(R1 = 5, R2 = 10, T1 = 10, T2 = 20) # Returns Q10; = 2 Q10(Q10 = 2.66, R1 = 5, T1 = 10, T2 = 20) # Returns R2; = 13.3 # My species has an MO2 of 9.5 umol/g/h at 10 *C. What MO2 should I expect at 13 *C? Q10(Q10 = 2, R1 = 9.5, T1 = 10, T2 = 13) # expect ~11.7 umol/g/h at 13 *C. # I measured MO2 at a spectrum of temperatures. What Q10 value best fits my data? Q10(R_vec = c(1, 2, 5, NA, 18, 33), T_vec = c(0, 10, 20, 30, 40, 50)) # A 100 g individual at 10 *C has an MO2 of 1270 umol/h. How much # would a 250 g individual likely consume at 14 *C? Q10(Q10 = 2, R1 = scale_MO2(mass_1 = 100, MO2_1 = 1270, mass_2 = 250), T1 = 10, T2 = 14) # Visualize MO2 scaling by mass and temperature: mass <- seq(10, 200, 10) temp <- 10:25 base_mass <- 50 base_temp <- 20 base_MO2 <- 750 mo2 <- outer(mass, temp, function(mass, temp){ scale_MO2(mass_1 = base_mass, mass_2 = mass, MO2_1 = Q10(Q10 = 2, R1 = base_MO2, T1 = base_temp, T2 = temp)) }) persp(mass, temp, mo2, xlab = 'Mass (g)', ylab = 'Temperature (*C)', zlab = 'MO2 (umol / hr)', theta = 35, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)
Provides tools to enable the researcher to more precisely conduct respirometry experiments. Strong emphasis is on aquatic respirometry. Tools focus on helping the researcher setup and conduct experiments. Analysis of the resulting data is not a focus since analyses are often specific to a particular setup, and thus are better created by the researcher individually. This package provides tools for intermittent, flow-through, and closed respirometry techniques.
Matthew A. Birk, [email protected]
Calculates the respiratory quotient (RQ), or ratio of CO2 produced to O2 consumed between observations. To calculate CO2 produced, either DIC or both pH and TA must be provided.
RQ( o2, o2_unit = "percent_a.s.", pH = NULL, TA = NULL, DIC = NULL, temp = 25, sal = 35, atm_pres = 1013.25 )
RQ( o2, o2_unit = "percent_a.s.", pH = NULL, TA = NULL, DIC = NULL, temp = 25, sal = 35, atm_pres = 1013.25 )
o2 |
a numeric vector of O2 values with a length of at least 2. |
o2_unit |
a string describing the unit used to measure |
pH |
pH (total scale). Elements must align with |
TA |
total alkalinity (umol / kg). May be either a vector with length equal to |
DIC |
dissolved inorganic carbon (umol / kg). Elements must align with |
temp |
temperature (°C). Default is 25 °C. |
sal |
salinity (psu). Default is 35 psu. |
atm_pres |
atmospheric pressure (mbar). Default is 1013.25 mbar. |
ratio of CO2 produced to O2 consumed.
If you want a rough estimate of RQ, but only have pH measurements, TA can be estimated from salinity using guess_TA
.
Matthew A. Birk, [email protected]
o2_observations <- c(21, 18, 14.5, 7) pH_observations <- c(8.05, 7.98, 7.86, 7.65) TA_observations <- c(2222, 2219, 2208, 2214) RQ(o2 = o2_observations, o2_unit = 'kPa', pH = pH_observations, TA = TA_observations, temp = 20, sal = 33) DIC_observations <- c(2222, 2250, 2284, 2355) RQ(o2 = o2_observations, o2_unit = 'kPa', DIC = DIC_observations) RQ(o2 = o2_observations, o2_unit = 'kPa', pH = pH_observations, TA = 2032)
o2_observations <- c(21, 18, 14.5, 7) pH_observations <- c(8.05, 7.98, 7.86, 7.65) TA_observations <- c(2222, 2219, 2208, 2214) RQ(o2 = o2_observations, o2_unit = 'kPa', pH = pH_observations, TA = TA_observations, temp = 20, sal = 33) DIC_observations <- c(2222, 2250, 2284, 2355) RQ(o2 = o2_observations, o2_unit = 'kPa', DIC = DIC_observations) RQ(o2 = o2_observations, o2_unit = 'kPa', pH = pH_observations, TA = 2032)
For most organisms, metabolic rate does not scale linearly, but rather according to a power curve. This function estimates MO2 or size of an individual organism given the MO2 and size of another individual of a different size. To mass-correct your MO2 data, plug in your desired mass in mass_2
and the output from calc_b
to the b
parameter.
scale_MO2(mass_1, MO2_1, mass_2, MO2_2, b = 0.75) scale_mo2(mass_1, MO2_1, mass_2, MO2_2, b = 0.75)
scale_MO2(mass_1, MO2_1, mass_2, MO2_2, b = 0.75) scale_mo2(mass_1, MO2_1, mass_2, MO2_2, b = 0.75)
mass_1 |
animal mass for |
MO2_1 |
metabolic rate for |
mass_2 |
animal mass for |
MO2_2 |
metabolic rate for |
b |
scaling coefficient for MO2. Default is 0.75. |
where b0
is species-specific normalization constant, M
is mass and b
is the scaling coefficient which is around 0.75 for many organisms.
For scaling of mass-specific metabolic rates, use something closer to b = -0.25
rather than b = 0.75
.
Matthew A. Birk, [email protected]
# I know a species has an SMR of 800 umol O2/h at 200 g. # What would be a likely SMR for a 300 g individual? scale_MO2(mass_1 = 200, MO2_1 = 800, mass_2 = 300) # Some squids have a much higher scaling coefficient: scale_MO2(mass_1 = 200, MO2_1 = 800, mass_2 = 300, b = 0.92) # A 100 g individual at 10 *C has an MO2 of 1270 umol/h. How much # would a 250 g individual likely consume at 14 *C? Q10(Q10 = 2, R1 = scale_MO2(mass_1 = 100, MO2_1 = 1270, mass_2 = 250), T1 = 10, T2 = 14) # Now I have data from real animals and I want to mass-correct them all to a 10 g animal. mass = 2:20 # obviously not real but you get the point mo2 = c(44.8, 41, 36, 35, 35, 33.5, 34.5, 40, 30, 23, 27, 30, 25.6, 27.8, 28, 24, 27, 28, 20) desired_mass = 10 b = calc_b(mass = mass, MO2 = mo2) scale_MO2(mass_1 = mass, MO2_1 = mo2, mass_2 = desired_mass, b = b$b) plot(mass, mo2, ylab = 'Raw MO2') # before plot(mass, scale_MO2(mass_1 = mass, MO2_1 = mo2, mass_2 = 10, b = b$b), ylab = 'Mass-corrected MO2') # after # Visualize MO2 scaling by mass and temperature: mass <- seq(10, 200, 10) temp <- 10:25 base_mass <- 50 base_temp <- 20 base_MO2 <- 750 mo2 <- outer(mass, temp, function(mass, temp){ scale_MO2(mass_1 = base_mass, mass_2 = mass, MO2_1 = Q10(Q10 = 2, R1 = base_MO2, T1 = base_temp, T2 = temp)) }) persp(mass, temp, mo2, xlab = 'Mass (g)', ylab = 'Temperature (*C)', zlab = 'MO2 (umol / hr)', theta = 35, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)
# I know a species has an SMR of 800 umol O2/h at 200 g. # What would be a likely SMR for a 300 g individual? scale_MO2(mass_1 = 200, MO2_1 = 800, mass_2 = 300) # Some squids have a much higher scaling coefficient: scale_MO2(mass_1 = 200, MO2_1 = 800, mass_2 = 300, b = 0.92) # A 100 g individual at 10 *C has an MO2 of 1270 umol/h. How much # would a 250 g individual likely consume at 14 *C? Q10(Q10 = 2, R1 = scale_MO2(mass_1 = 100, MO2_1 = 1270, mass_2 = 250), T1 = 10, T2 = 14) # Now I have data from real animals and I want to mass-correct them all to a 10 g animal. mass = 2:20 # obviously not real but you get the point mo2 = c(44.8, 41, 36, 35, 35, 33.5, 34.5, 40, 30, 23, 27, 30, 25.6, 27.8, 28, 24, 27, 28, 20) desired_mass = 10 b = calc_b(mass = mass, MO2 = mo2) scale_MO2(mass_1 = mass, MO2_1 = mo2, mass_2 = desired_mass, b = b$b) plot(mass, mo2, ylab = 'Raw MO2') # before plot(mass, scale_MO2(mass_1 = mass, MO2_1 = mo2, mass_2 = 10, b = b$b), ylab = 'Mass-corrected MO2') # after # Visualize MO2 scaling by mass and temperature: mass <- seq(10, 200, 10) temp <- 10:25 base_mass <- 50 base_temp <- 20 base_MO2 <- 750 mo2 <- outer(mass, temp, function(mass, temp){ scale_MO2(mass_1 = base_mass, mass_2 = mass, MO2_1 = Q10(Q10 = 2, R1 = base_MO2, T1 = base_temp, T2 = temp)) }) persp(mass, temp, mo2, xlab = 'Mass (g)', ylab = 'Temperature (*C)', zlab = 'MO2 (umol / hr)', theta = 35, phi = 15, expand = 0.5, ticktype = 'detailed', nticks = 10)