Title: | Greenhouse Gas Flux Calculation from Chamber Measurements |
---|---|
Description: | Functions for greenhouse gas flux calculation from chamber measurements. |
Authors: | Roland Fuss [aut, cre] , Roman Hueppi [ctb] |
Maintainer: | Roland Fuss <[email protected]> |
License: | GPL (>= 2) |
Version: | 0.7 |
Built: | 2024-11-15 03:51:03 UTC |
Source: | https://github.com/cran/gasfluxes |
Aggregate a time series of fluxes to a cummulative flux value.
agg.fluxes(fluxes, datetimes, timeunit = "hours")
agg.fluxes(fluxes, datetimes, timeunit = "hours")
fluxes |
flux values |
datetimes |
datetime values (POSIXct or POSIXlt) |
timeunit |
the unit of time (denominator of the flux unit), supported are the explicit units supported by |
The function uses linear interpolation. The unit of the cumulative flux is [fluxes] * timeunit. NA values are removed and values sorted according to time order. If less then two non-NA value pairs are provided, NA is returned for the cumlative flux.
A one-row data.frame with columns
flux |
the cumulative flux |
from |
the start of the cumulation period |
to |
the end of the cumulation period |
The return value being a data.frame is useful, when the function is used for "split-apply-combine" type operations to calculate groupwise cumulated values, e.g., using package data.table.
#Some random example data datetimes <- Sys.time() + (1:20)/2*24*3600 set.seed(42) fluxes <- rlnorm(20, 5) agg.fluxes(fluxes, datetimes)
#Some random example data datetimes <- Sys.time() + (1:20)/2*24*3600 set.seed(42) fluxes <- rlnorm(20, 5) agg.fluxes(fluxes, datetimes)
This is the complementary error function.
erfc(x)
erfc(x)
x |
a numeric vector |
A numeric vector, i.e., the erfc values.
A dataset containing data from 1329 chamber N2O flux measurements.
A data.table with 5300 rows and 5 variables:
serie: ID of flux measurement
V: Volume (normalized by area, i.e., the height in m)
A: Area (always 1)
time: closing time in h
C: N2O concentration in mg N / m^3
own data (anonymized by not including site and treatment information)
A wrapper function for convenient flux calculation.
gasfluxes( dat, .id = "ID", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("linear", "robust linear", "HMR", "NDFE"), k_HMR = log(1.5), k_NDFE = log(0.01), verbose = TRUE, plot = TRUE, select, maxiter = 100, ... )
gasfluxes( dat, .id = "ID", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("linear", "robust linear", "HMR", "NDFE"), k_HMR = log(1.5), k_NDFE = log(0.01), verbose = TRUE, plot = TRUE, select, maxiter = 100, ... )
dat |
a data.frame or data.table with data from flux measurements. |
.id |
character vector specifying the columns to be used as ID, multiple ID columns are possible. |
.V |
character specifying the column containing chamber volume values. |
.A |
character specifying the column containing chamber area values. |
.times |
character specifying the column containing chamber closing time values. |
.C |
character specifying the column containing concentration values. |
methods |
character; which methods to use for flux estimation. See details for available methods. |
k_HMR |
starting value for |
k_NDFE |
starting value for |
verbose |
logical; print progress messages? |
plot |
create a PDF with plots in the working directory if |
select |
deprecated; please use function |
maxiter |
see |
... |
further parameters |
Available methods are
"linear": | lin.fit
|
"robust linear": | rlin.fit
|
"HMR": | HMR.fit
|
"NDFE": | NDFE.fit
|
Specifying other methods results in an error.
The default starting values for "HMR" and "NDFE", and
, resp., assume that time is in hours. If you use a different time unit, you should adjust them accordingly.
Note that
nls
is used internally by these functions and thus they should not be used with artificial "zero-residual" data.
The input data.frame or data.table should be in the following format:
serie V A time C 1: ID1 0.522625 1 0.0000000 0.3317823 2: ID1 0.522625 1 0.3333333 0.3304053 3: ID1 0.522625 1 0.6666667 0.3394311 4: ID1 0.522625 1 1.0000000 0.4469102 5: ID2 0.523625 1 0.0000000 0.4572708
However, more than one ID column are possible. E.g., the first ID column could be the plot and a second ID column could be the date. Keep in mind that the combination of IDs must be a unique identifier for each flux measurement.
Units of the output depend on input units. It's recommended to use [V] = m^3, [A] = m^2, [time] = h, [C] = [mass or mol]/m^3, which results in [f0] = [mass or mol]/m^2/h. Since all algorithms use V/A, A can be input as 1 and V as the chamber height.
A data.table with the results of the flux calculation. See the documentation of the fitting functions for details. If a selection algorithm has been specified, the last columns are the selected flux estimate, the corresponding standard error and p-value and the method with which the selected flux was estimated.
selectfluxes
for flux selection
## Not run: #compare result of original HMR with plinear HMR data(fluxMeas) res <- gasfluxes(fluxMeas[1:400,], .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("HMR"), verbose = TRUE) #number of successful fits res[, sum(!is.na(HMR.kappa))] res <- gasfluxes(fluxMeas, .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = "HMR", verbose = TRUE) # Error: time not sorted in flux ID ID556. # Investigate the problem: fluxMeas[serie %in% c("ID555", "ID556", "ID557")] # serie V A time C # 1: ID555 0.551625 1 0.0000000 0.3884388 # 2: ID555 0.551625 1 0.3333333 0.4125270 # 3: ID555 0.551625 1 0.6666667 0.3714207 # 4: ID555 0.551625 1 1.0000000 0.3735092 # 5: ID556 0.524250 1 0.0000000 0.3638239 # 6: ID556 0.524250 1 0.3333333 0.3520481 # 7: ID556 0.524250 1 0.6666667 0.3551644 # 8: ID557 0.528375 1 0.0500000 0.3954601 # 9: ID556 0.524250 1 0.0000000 0.3839834 #10: ID557 0.528375 1 0.3333333 0.3967269 #11: ID557 0.528375 1 0.6666667 0.3764967 #12: ID557 0.528375 1 1.0000000 0.3973055 # some mixup of IDs and times # usually an input or Excel error during data preparation # investigate and fix ## End(Not run)
## Not run: #compare result of original HMR with plinear HMR data(fluxMeas) res <- gasfluxes(fluxMeas[1:400,], .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("HMR"), verbose = TRUE) #number of successful fits res[, sum(!is.na(HMR.kappa))] res <- gasfluxes(fluxMeas, .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = "HMR", verbose = TRUE) # Error: time not sorted in flux ID ID556. # Investigate the problem: fluxMeas[serie %in% c("ID555", "ID556", "ID557")] # serie V A time C # 1: ID555 0.551625 1 0.0000000 0.3884388 # 2: ID555 0.551625 1 0.3333333 0.4125270 # 3: ID555 0.551625 1 0.6666667 0.3714207 # 4: ID555 0.551625 1 1.0000000 0.3735092 # 5: ID556 0.524250 1 0.0000000 0.3638239 # 6: ID556 0.524250 1 0.3333333 0.3520481 # 7: ID556 0.524250 1 0.6666667 0.3551644 # 8: ID557 0.528375 1 0.0500000 0.3954601 # 9: ID556 0.524250 1 0.0000000 0.3839834 #10: ID557 0.528375 1 0.3333333 0.3967269 #11: ID557 0.528375 1 0.6666667 0.3764967 #12: ID557 0.528375 1 1.0000000 0.3973055 # some mixup of IDs and times # usually an input or Excel error during data preparation # investigate and fix ## End(Not run)
Fit the HMR model using the Golub-Pereyra algorithm for partially linear least-squares models.
HMR.fit( t, C, A = 1, V, serie = "", k = log(1.5), verbose = TRUE, plot = FALSE, maxiter = 100, ... )
HMR.fit( t, C, A = 1, V, serie = "", k = log(1.5), verbose = TRUE, plot = FALSE, maxiter = 100, ... )
t |
time values (usually in hours) |
C |
concentration values |
A |
area covered by the chamber |
V |
effective volume of the chamber |
serie |
id of the flux measurement |
k |
starting value for nls function |
verbose |
logical, TRUE prints message after each flux calculation |
plot |
logical, mainly intended for use in |
maxiter |
see |
... |
further parameters, currently none |
The HMR model (Pedersen et al., 2010) is .
To ensure the lower bound
, the substitution
is used. The resulting reparameterized model is then
fit using
nls
with algorithm = "plinear"
. This is computationally more efficient than the manual implementation in the HMR package and results
in almost identical flux values. Flux standard errors and p-values differ strongly from those reported by the HMR package <= version 0.3.1,
but are equal to those reported by later versions.
The default starting value assumes that time is in hours. If you use a different time unit, you should adjust it accordingly.
There have been demands to return the initial concentration as predicted by the model as this is useful for checking plausibility. However,
this can be easily calculated from the parameters and the equation of the model by setting t = 0, i.e., .
Note that nls
is used internally and thus this function should not be used with artificial "zero-residual" data.
A list of
f0 |
flux estimate |
f0.se |
standard error of flux estimate |
f0.p |
p-value of flux estimate |
kappa , phi
|
other parameters of the HMR model |
AIC |
Akaike information criterion |
AICc |
Akaike information criterion with small sample correction |
RSE |
residual standard error (sigma from summary.nls) |
diagnostics |
error or warning messages |
Pedersen, A.R., Petersen, S.O., Schelde, K., 2010. A comprehensive approach to soil-atmosphere trace-gas flux estimation with static chambers. European Journal of Soil Science 61(6), 888-902.
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 341, 352, 359) print(fit <- HMR.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$phi + fit$f0 * exp(-fit$kappa * x)/(-fit$kappa*0.3)}, from = 0, to = 1, add = TRUE) ## Not run: #a dataset of 1329 chamber N2O flux measurements data(fluxMeas) fluxMeas[, n := length(time), by=serie] print(fluxMeas) fluxes <- fluxMeas[n > 3, HMR.fit(time, C, A, V, serie), by=serie] print(fluxes) plot(f0.se ~ f0, data = fluxes) #one very large f0.se value (and several infinite ones not shown in the plot) fluxes[is.finite(f0.se),][which.max(f0.se),] plot(C~time, data=fluxMeas[serie=="ID940",]) print(tmp <- fluxes[is.finite(f0.se),][which.max(f0.se),]) curve({tmp[, phi] + tmp[, f0] * exp(-tmp[, kappa] * x)/ (-tmp[, kappa]*fluxMeas[serie=="ID940", V[1]]/ fluxMeas[serie=="ID940",A[1]])}, from = 0, to = 1, add = TRUE) plot(f0.se ~ f0, data = fluxes[f0.se < 1e4,], pch = 16) boxplot(fluxes[f0.se < 1e4, sqrt(f0.se)]) ## End(Not run)
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 341, 352, 359) print(fit <- HMR.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$phi + fit$f0 * exp(-fit$kappa * x)/(-fit$kappa*0.3)}, from = 0, to = 1, add = TRUE) ## Not run: #a dataset of 1329 chamber N2O flux measurements data(fluxMeas) fluxMeas[, n := length(time), by=serie] print(fluxMeas) fluxes <- fluxMeas[n > 3, HMR.fit(time, C, A, V, serie), by=serie] print(fluxes) plot(f0.se ~ f0, data = fluxes) #one very large f0.se value (and several infinite ones not shown in the plot) fluxes[is.finite(f0.se),][which.max(f0.se),] plot(C~time, data=fluxMeas[serie=="ID940",]) print(tmp <- fluxes[is.finite(f0.se),][which.max(f0.se),]) curve({tmp[, phi] + tmp[, f0] * exp(-tmp[, kappa] * x)/ (-tmp[, kappa]*fluxMeas[serie=="ID940", V[1]]/ fluxMeas[serie=="ID940",A[1]])}, from = 0, to = 1, add = TRUE) plot(f0.se ~ f0, data = fluxes[f0.se < 1e4,], pch = 16) boxplot(fluxes[f0.se < 1e4, sqrt(f0.se)]) ## End(Not run)
Fit a linear model to concentration - time data.
lin.fit(t, C, A = 1, V, serie = "", verbose = TRUE, plot = FALSE, ...)
lin.fit(t, C, A = 1, V, serie = "", verbose = TRUE, plot = FALSE, ...)
t |
time values (usually in hours) |
C |
concentration values |
A |
area covered by the chamber |
V |
effective volume of the chamber |
serie |
id of the flux measurement |
verbose |
logical, TRUE prints message after each flux calculation |
plot |
logical, mainly intended for use in |
... |
further parameters, currently none |
This is basically a wrapper of R's OLS fitting facilities. For now lm
(and methods for objects of class "lm") is used,
but this may change to more efficient alternatives in later versions.
A list of
f0 |
flux estimate |
f0.se |
standard error of flux estimate |
f0.p |
p-value of flux estimate |
C0 |
estimated concentration at t = 0 (intercept) |
AIC |
Akaike information criterion |
AICc |
Akaike information criterion with small sample correction |
RSE |
residual standard error (sigma from summary.nls) |
r |
Pearson's correlation coefficient |
diagnostics |
error or warning messages |
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 341, 352, 359) print(fit <- lin.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$f0/0.3 * x + fit$C0}, from = 0, to = 1, add = TRUE)
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 341, 352, 359) print(fit <- lin.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$f0/0.3 * x + fit$C0}, from = 0, to = 1, add = TRUE)
Fit the the non-steady-state diffusive flux extimator model using the Golub-Pereyra algorithm for partially linear least-squares models.
NDFE.fit( t, C, A = 1, V, serie = "", k = log(0.01), verbose = TRUE, plot = FALSE, maxiter = 100, ... )
NDFE.fit( t, C, A = 1, V, serie = "", k = log(0.01), verbose = TRUE, plot = FALSE, maxiter = 100, ... )
t |
time values (usually in hours) |
C |
concentration values |
A |
area covered by the chamber |
V |
effective volume of the chamber |
serie |
id of the flux measurement |
k |
starting value for nls function |
verbose |
logical, TRUE prints message after each flux calculation |
plot |
logical, mainly intended for use in |
maxiter |
see |
... |
further parameters, currently none |
The NDFE model (Livingston et al., 2006) is .
To ensure the lower bound
, the substituion
is used. The resulting reparameterized model is then
fit using
nls
with algorithm = "plinear"
.
Note that according to the reference the model is not valid for negative fluxes. Warning: This function does not check if fluxes are positive. It's left to the user to handle negative fluxes.
The default starting value assumes that time is in hours. If you use a different time unit, you should adjust it accordingly.
Note that nls
is used internally and thus this function should not be used with artificial "zero-residual" data.
A list of
f0 |
flux estimate |
f0.se |
standard error of flux estimate |
f0.p |
p-value of flux estimate |
C0 , tau
|
other parameters of the NDFE model |
AIC |
Akaike information criterion |
AICc |
Akaike information criterion with small sample correction |
RSE |
residual standard error (sigma from summary.nls) |
diagnostics |
error or warning messages |
Livingston, G.P., Hutchinson, G.L., Spartalian, K., 2006. Trace gas emission in chambers: A non-steady-state diffusion model. Soil Sci. Soc. Am. J. 70(5), 1459-1469.
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 340, 355, 362) print(fit <- NDFE.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$C0+fit$f0*fit$tau*1/0.3*(2/sqrt(pi)*sqrt(x/fit$tau)+ exp(x/fit$tau)*erfc(sqrt(x/fit$tau))-1)}, from = 0, to = 1, add = TRUE) #note that the flux estimate is very uncertain because #there are no data points in the region of high curvature
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 340, 355, 362) print(fit <- NDFE.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$C0+fit$f0*fit$tau*1/0.3*(2/sqrt(pi)*sqrt(x/fit$tau)+ exp(x/fit$tau)*erfc(sqrt(x/fit$tau))-1)}, from = 0, to = 1, add = TRUE) #note that the flux estimate is very uncertain because #there are no data points in the region of high curvature
Fit a linear model to concentration - time data using robust methods.
rlin.fit(t, C, A = 1, V, serie = "", verbose = TRUE, plot = FALSE, ...)
rlin.fit(t, C, A = 1, V, serie = "", verbose = TRUE, plot = FALSE, ...)
t |
time values (usually in hours) |
C |
concentration values |
A |
area covered by the chamber |
V |
effective volume of the chamber |
serie |
id of the flux measurement |
verbose |
logical, TRUE prints message after each flux calculation |
plot |
logical, mainly intended for use in |
... |
further parameters, currently none |
This is basically a wrapper of rlm
using the Huber M estimator. This function never weights the first or last time point with zero with very few data points. However, there might exist "better" robust regression methods for flux estimation.
A list of
f0 |
flux estimate |
f0.se |
standard error of flux estimate |
f0.p |
p-value of flux estimate |
C0 |
estimated concentration at t = 0 (intercept) |
weights |
robustness weights |
diagnostics |
error or warning messages |
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 330, 315, 351) print(fit <- rlin.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$f0/0.3 * x + fit$C0}, from = 0, to = 1, add = TRUE)
#a single fit t <- c(0, 1/3, 2/3, 1) C <- c(320, 330, 315, 351) print(fit <- rlin.fit(t, C, 1, 0.3, "a")) plot(C ~ t) curve({fit$f0/0.3 * x + fit$C0}, from = 0, to = 1, add = TRUE)
Selects the appropriate flux estimate from linear, robust linear and non-linear calculated fluxes.
selectfluxes(dat, select, f.detect = NULL, t.meas = NULL, tol = 5e-05, ...)
selectfluxes(dat, select, f.detect = NULL, t.meas = NULL, tol = 5e-05, ...)
dat |
a data.table as returned by |
select |
character; specify a ruleset for selection of the final flux value, see details. |
f.detect |
detection limit for HMR method. This can be determined by a simple simulation (see examples) or for four data points the approximation in Parkin et al. (2012) can be used. |
t.meas |
a vector or single value giving the measurement time factor that relates to kappa.max. It is suggested to use the time difference between the first and last sample taken from the closed chamber. The unit should be consistent with the units of |
tol |
the relative tolerance |
... |
further parameters |
Available selection algorithms currently are
The selection algorithm restricts the use of HMR by imposing a maximal value for kappa "kappa.max", depending on the quotient of the linear flux estimate and the minimal detectable flux (f.detect), as well as the chamber closure time (t.meas). kappa.max = f.lin/f.detect/t.meas. This is currently the recommended algorithm. Note that the algorithm was developed for predominantly positive fluxes (such as N2O fluxes). If data with considerable gas uptake is analyzed, the algorithm needs to be modified, which currently means the user needs to implement it themselves.
Other selection algorithms could be implemented, but selection can always be done as a postprocessing step. E.g., if many data points are available for each flux measurement it is probably most sensible to use AICc.
A data.table with the with following columns added to the function input: selected flux estimate, the corresponding standard error and p-value and the method with which the selected flux was estimated. For the "kappa.max" method the "kappa.max" values are included. These columns are also added to the input data.table by reference.
Parkin, T.B., Venterea, R.T., Hargreaves, S.K., 2012. Calculating the Detection Limits of Chamber-based Soil Greenhouse Gas Flux Measurements. Journal of Environmental Quality 41, 705-715.
Hueppi, R., Felber, R., Krauss, M., Six, J., Leifeld, J., Fuss, R., 2018. Restricting the nonlinearity parameter in soil greenhouse gas flux calculation for more reliable flux estimates. PLOS ONE 13(7): e0200876. https://doi.org/10.1371/journal.pone.0200876
## Not run: res <- gasfluxes(fluxMeas[1:499], .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("linear", "robust linear", "HMR"), verbose = FALSE, plot = FALSE) ### estimate f.detect by simulation ### #ambient concentration: C0 <- 320/1000 * 28 * 273.15 / 22.4 / (273.15 + 15) #mg N / m^3 #uncertainty of GC measurement: sdGC <- 5/1000 * 28 * 273.15 / 22.4 / (273.15 + 15) #mg N / m^3 #create simulated concentrations corresponding to 1 hour flux measurements with zero fluxes: set.seed(42) sim <- data.frame(t = seq(0, 1, length.out = 4), C = rnorm(4e3, mean = C0, sd = sdGC), id = rep(1:1e3, each = 4), A = 1, V = 0.52) #fit HMR model: simflux <- gasfluxes(sim, .id = "id", .times = "t", methods = c("HMR", "linear"), plot = FALSE) simflux[, f0 := HMR.f0] simflux[is.na(f0), f0 := linear.f0] #dection limit as 97.5 % quantile (95 % confidence): f.detect <- simflux[, quantile(f0, 0.975)] #0.03 mg N / m^2 / h # example using the kappa.max (ref. Hueppi et al., 2018) with a single t.meas value t.meas <- max(fluxMeas$time[1:499]) #1 selectfluxes(res, "kappa.max", f.detect = f.detect, t.meas = t.meas) res[method == "HMR", .N] # 11 # example using the kappa.max with a vector for t.meas t.meas <- fluxMeas[1:499][, max(time), by = serie][["V1"]] selectfluxes(res, "kappa.max", f.detect = f.detect, t.meas = t.meas) res[method == "HMR", .N] # 10 ## End(Not run)
## Not run: res <- gasfluxes(fluxMeas[1:499], .id = "serie", .V = "V", .A = "A", .times = "time", .C = "C", methods = c("linear", "robust linear", "HMR"), verbose = FALSE, plot = FALSE) ### estimate f.detect by simulation ### #ambient concentration: C0 <- 320/1000 * 28 * 273.15 / 22.4 / (273.15 + 15) #mg N / m^3 #uncertainty of GC measurement: sdGC <- 5/1000 * 28 * 273.15 / 22.4 / (273.15 + 15) #mg N / m^3 #create simulated concentrations corresponding to 1 hour flux measurements with zero fluxes: set.seed(42) sim <- data.frame(t = seq(0, 1, length.out = 4), C = rnorm(4e3, mean = C0, sd = sdGC), id = rep(1:1e3, each = 4), A = 1, V = 0.52) #fit HMR model: simflux <- gasfluxes(sim, .id = "id", .times = "t", methods = c("HMR", "linear"), plot = FALSE) simflux[, f0 := HMR.f0] simflux[is.na(f0), f0 := linear.f0] #dection limit as 97.5 % quantile (95 % confidence): f.detect <- simflux[, quantile(f0, 0.975)] #0.03 mg N / m^2 / h # example using the kappa.max (ref. Hueppi et al., 2018) with a single t.meas value t.meas <- max(fluxMeas$time[1:499]) #1 selectfluxes(res, "kappa.max", f.detect = f.detect, t.meas = t.meas) res[method == "HMR", .N] # 11 # example using the kappa.max with a vector for t.meas t.meas <- fluxMeas[1:499][, max(time), by = serie][["V1"]] selectfluxes(res, "kappa.max", f.detect = f.detect, t.meas = t.meas) res[method == "HMR", .N] # 10 ## End(Not run)