Nothing
# CHNOSZ/util-affinity.R
# Helper functions for affinity()
## If this file is interactively sourced, the following are also needed to provide unexported functions:
#source("util.units.R")
slice.affinity <- function(affinity, d = 1, i = 1) {
# Take a slice of affinity along one dimension
a <- affinity
for(j in 1:length(a$values)) {
# Preserve the dimensions (especially: names(mydim))
# - fix for change in behavior of aperm in R-devel 2015-11-17
mydim <- dim(a$values[[j]])
a$values[[j]] <- as.array(slice(a$values[[j]], d = d, i = i))
# The dimension from which we take the slice vanishes
dim(a$values[[j]]) <- mydim[-d]
}
return(a)
}
### Unexported functions ###
energy <- function(what, vars, vals, lims, T = 298.15, P = "Psat", IS = 0, sout = NULL, exceed.Ttr = FALSE, exceed.rhomin = FALSE, transect = FALSE) {
# 20090329 Dxtracted from affinity() and made to
# deal with >2 dimensions (variables)
# Calculate "what" property
# logK - logK of formation reactions
# logact.basis - logarithms of activities of basis species in reactions
# logact.species - logarithms of activities of species in reactions
# logQ - logQ of formation reactions
# A - A/2.303RT of formation reactions
# "vars": vector of variables (T, P, basisnames, IS)
# "vals": list of values for each variable
# "lims": used to get the dimensions (resolution for each variable)
### Some argument checking
if(length(unique(vars)) != length(vars)) stop("please supply unique variable names")
## Basis definition / number of basis species
mybasis <- basis()
nbasis <- nrow(mybasis)
## Species definition / number of species
myspecies <- get("thermo", CHNOSZ)$species
if(is.character(what)) {
if(is.null(myspecies)) stop('species properties requested, but species have not been defined')
nspecies <- nrow(myspecies)
if(!identical(what, "logact.basis")) ispecies <- 1:nspecies
}
## The dimensions of our arrays
resfun <- function(lim) lim[3]
mydim <- sapply(lims, resfun)
if(transect) {
if(!isTRUE(all.equal(min(mydim), max(mydim)))) stop("variables define a transect but their lengths are not all equal")
mydim <- mydim[[1]]
}
## The number of dimensions we have
if(transect) nd <- 1 else nd <- length(vars) # == length(mydim)
## Basis names / which vars denote basis species
basisnames <- rownames(mybasis)
ibasisvar <- match(vars, basisnames)
varisbasis <- !is.na(ibasisvar)
ibasisvar <- ibasisvar[!is.na(ibasisvar)]
## Which vars are in P, T, IS
varissubcrt <- vars %in% c("P", "T", "IS")
if(sum(varissubcrt) > 2) stop("only up to 2 of P,T,IS are supported")
## Categorize the basis species:
# 0 - not in the vars; 1 - one of the vars
ibasis <- 1:nbasis
ibasis0 <- ibasis[!ibasis %in% ibasisvar]
ibasis1 <- ibasis[ibasis %in% ibasisvar]
if(identical(what, "logact.basis")) ispecies <- ibasis
## What subcrt variable is used to make a 2-D grid?
workaround.IS <- FALSE
if(sum(varissubcrt) > 1 & !transect) {
grid <- vars[varissubcrt][1]
if("IS" %in% vars) {
if(grid != "IS") workaround.IS <- TRUE
grid <- "IS"
}
} else grid <- NULL
### Done argument processing
### Function to index the variables in a permuted order
# by swapping ivar for the first
# e.g. for nd = 5, ivars(4) = c(4, 2, 3, 1, 5)
ivars <- function(ivar, iv = NULL) {
if(nd == 0) return(1)
if(is.null(iv)) iv <- 1:nd
iv.1 <- iv[1]
iv[1] <- ivar
iv[ivar] <- iv.1
return(iv)
}
### Functions for logact / logQ
# A basis species not in var
logact.basis0.fun <- function(ibasis) {
logact <- mybasis$logact[ibasis]
# for the case where this basis species is buffered
if(!can.be.numeric(logact)) logact <- 0
else logact <- as.numeric(logact)
return(array(logact, mydim))
}
# A basis species in var
logact.basis1.fun <- function(ivar) {
dim.in <- dim(vals[[ivar]])
if(is.null(dim.in)) dim.in <- 0
# why doesn't this work?
# if(identical(dim.in, mydim))
if(all(dim.in == mydim) | transect) return(vals[[ivar]])
else return(aperm(array(vals[[ivar]], mydim[ivars(ivar)]), ivars(ivar)))
}
# Any basis species
logact.basis.fun <- function(ibasis) {
if(ibasis %in% ibasis0) return(logact.basis0.fun(ibasis))
else return(logact.basis1.fun(match(basisnames[ibasis], vars)))
}
# All basis species
logact.basis <- function() lapply(ibasis, logact.basis.fun)
# logact of a single species
logact.species.fun <- function(ispecies) array(myspecies$logact[ispecies], mydim)
## Contributions to logQ
# From a single basis species
logQ.basis.fun <- function(ibasis, coeff) - coeff * logact.basis.fun(ibasis)
# From all basis species in a single formation reaction
logQ.basis.species <- function(ispecies)
Reduce("+", mapply(logQ.basis.fun, ibasis, myspecies[ispecies, 1:nbasis], SIMPLIFY = FALSE))
# All basis species in all reactions
logQ.basis <- function() mapply(logQ.basis.species, 1:nspecies, SIMPLIFY = FALSE)
# By a single species
logQ.species.fun <- function(ispecies, coeff) coeff * logact.species.fun(ispecies)
# By all species
logQ.species <- function()
mapply(logQ.species.fun, 1:nspecies, 1, SIMPLIFY = FALSE)
# Total logQ of all reactions
logQ <- function() lsum(logQ.basis(), logQ.species())
### Function for calling subcrt
sout.fun <- function(property = "logK") {
species <- c(mybasis$ispecies, myspecies$ispecies)
if(!is.null(sout)) {
# Extract the needed species from a provided sout 20190131
isout <- match(species, sout$species$ispecies)
this.sout <- sout
this.sout$species <- this.sout$species[isout, ]
this.sout$out <- this.sout$out[isout]
return(this.sout)
} else {
## subcrt arguments
if("T" %in% vars) T <- vals[[which(vars == "T")]]
if("P" %in% vars) P <- vals[[which(vars == "P")]]
if("IS" %in% vars) IS <- vals[[which(vars == "IS")]]
s.args <- list(species = species, property = property, T = T, P = P, IS = IS, grid = grid,
convert = FALSE, exceed.Ttr = exceed.Ttr, exceed.rhomin = exceed.rhomin)
sout <- do.call("subcrt", s.args)
# 20191210 workaround a limitation in subcrt(): only IS (if present) can be the 'grid' variable
if(workaround.IS) {
lenIS <- length(IS)
# Only T or P has length > 1
lenTP <- max(length(T), length(P))
# Make an indexing matrix "byrow" then vectorize it (not byrow) to get permutation indices
indmat <- matrix(1:(lenIS * lenTP), nrow = lenIS, byrow = TRUE)
ind <- as.vector(indmat)
sout$out <- lapply(sout$out, function(x) x[indmat, ])
}
# Species indices are updated by subcrt() for minerals with polymorphic transitions
# e.g. i <- info("chalcocite"); subcrt(i, T = 200)$species$ispecies == i + 1
# so we should keep the original species index to be able to find the species in a provided 'sout'
# (noted for Mosaic diagram section of anintro.Rmd 20190203)
sout$species$ispecies <- species
return(sout)
}
}
### Functions for logK/subcrt props
# The logK contribution by any species or basis species
X.species <- function(ispecies, coeff, X) coeff * sout$out[[ispecies]][, names(sout$out[[ispecies]]) == X]
# The logK contribution by all basis species in a reaction
X.basis <- function(ispecies, X) Reduce("+", mapply(X.species, ibasis, -myspecies[ispecies, ibasis], X, SIMPLIFY = FALSE))
# The logK of any reaction
X.reaction <- function(ispecies, X) X.species((ispecies+nbasis), 1, X) + X.basis(ispecies, X)
# To get logK or subcrt props or other values into the dimensions we are using
dim.fun <- function(x, idim = NULL) {
if(is.null(idim)) {
if(transect) idim <- 1
else if(is.null(grid)) {
# One of T, P, IS
ivar <- which(vars %in% c("T", "P", "IS"))
if(length(ivar) == 0) ivar <- 1
idim <- ivars(ivar)
} else {
# Two of T, P, IS
ivar1 <- which(varissubcrt)[1]
ivar2 <- which(varissubcrt)[2]
idim <- ivars(ivar2, ivars(ivar1))
}
}
return(aperm(array(x, mydim[idim]), idim))
}
# Properties of all species
X.fun <- function(X) lapply(lapply(ispecies, X.reaction, X), dim.fun)
logK <- function() lapply(ispecies, X.reaction, "logK")
# A/2.303RT
A <- function() {
out <- mapply(`-`, X.fun("logK"), logQ(), SIMPLIFY = FALSE)
# Deal with affinities of protein ionization here 20120527
if("H+" %in% rownames(mybasis)) {
# Which species are proteins
isprotein <- grepl("_", myspecies$name)
if(any(isprotein)) {
if(get("thermo", CHNOSZ)$opt$ionize.aa) {
message("affinity: ionizing proteins ...")
# The rownumbers in thermo()$protein
ip <- pinfo(myspecies$name[isprotein])
# Get the affinity of ionization
iHplus <- match("H+", rownames(mybasis))
# as.numeric is needed in case the logact column is character mode
# due to buffer definition (that means we can't do pH buffers)
pH <- -as.numeric(mybasis$logact[iHplus])
A.ionization <- A.ionization(ip, vars, vals, T = T, P = P, pH = pH, transect = transect)
# Add it to the affinities of formation reactions of the non-ionized proteins
out[isprotein] <- lsum(out[isprotein], A.ionization)
} else message("affinity: NOT ionizing proteins because thermo()$opt$ionize.aa is FALSE")
}
}
return(out)
}
### Call the necessary functions
if(!is.character(what)) {
# Expand numeric values into our dimensions
# (used by energy.args() for calculating pe = f(Eh, T) )
# TODO: document that sout here denotes the dimension
# we're expanding into
return(dim.fun(what, ivars(sout$out)))
} else if(what %in% c('G', 'H', 'S', 'Cp', 'V', 'E', 'kT', 'logK')) {
# Get subcrt properties for reactions
sout <- sout.fun(what)
a <- X.fun(what)
} else if(length(agrep("species", what)) > 0) {
# Get subcrt properties for species
# e.g. what = G.species, Cp.species etc
mywhat <- s2c(what, sep = ".", keep.sep = FALSE)[1]
sout <- sout.fun(mywhat)
a <- lapply(mapply(X.species, ispecies+nbasis, 1, mywhat, SIMPLIFY = FALSE), dim.fun)
} else {
# Get affinities or logact.basis, logact.species or logQ
if(what == "A") sout <- sout.fun("logK")
a <- do.call(what, list())
}
### Use species indices as the names
if(what == "logact.basis") names(a) <- mybasis$ispecies
else names(a) <- myspecies$ispecies
### Return the results
return(list(sout = sout, a = a))
}
energy.args <- function(args, transect = FALSE) {
## Extracted from affinity() and modified 20090329 jmd
# Takes a list of arguments which define the dimensions
# over which to calculate logQ, logK and affinity
# The names should be T, P, IS and names of basis species
# (or pH, pe, Eh)
thermo <- get("thermo", CHNOSZ)
## Inputs are like c(T1, T2, res)
# and outputs are like seq(T1, T2, length.out = res)
# unless transect: do the variables specify a transect? 20090627
transect <- any(transect, any(sapply(args, length) > 3))
## Convert T, P args and take care of
# Single values for T, P or IS
T <- 298.15
P <- "Psat"
IS <- 0
T.is.var <- P.is.var <- IS.is.var <- FALSE
arg.is.T <- names(args) == "T"
arg.is.P <- names(args) == "P"
arg.is.IS <- names(args) == "IS"
if(any(arg.is.T)) {
T <- args[[which(arg.is.T)]]
if(length(T) > 1) T.is.var <- TRUE
if(transect) args[[which(arg.is.T)]] <- T <- envert(T, 'K')
else args[[which(arg.is.T)]][1:2] <- T[1:2] <- envert(T[1:2], 'K')
T <- T[!is.na(T)]
}
if(any(arg.is.P)) {
P <- args[[which(arg.is.P)]]
if(length(P) > 1) P.is.var <- TRUE
if(!identical(P, "Psat")) {
if(transect) args[[which(arg.is.P)]] <- P <- envert(P, 'bar')
else args[[which(arg.is.P)]][1:2] <- P[1:2] <- envert(P[1:2], 'bar')
}
P <- P[!is.na(P)]
}
if(any(arg.is.IS)) {
IS <- args[[which(arg.is.IS)]]
if(length(IS) > 1) IS.is.var <- TRUE
}
# Report non-variables to user
if(!T.is.var) {
Tunits <- T.units()
if(Tunits == "C") Tunits <- "\u00BAC"
message('affinity: temperature is ', outvert(T, 'K'), ' ', Tunits)
}
if(!P.is.var) {
if(identical(P, "Psat")) message("affinity: pressure is Psat")
else message('affinity: pressure is ', outvert(P, 'bar'), ' ', P.units())
}
if(!IS.is.var & !identical(IS, 0)) message('affinity: ionic strength is ', IS)
# Default value for resolution
res <- 256
# Where we store the output
what <- "A"
vars <- character()
vals <- list(NA)
# This needs to have 1 as the third component because
# energy() uses it to build an array with the given dimension
lims <- list(c(NA, NA, 1))
# Clean out non-variables
if(any(arg.is.T) & !T.is.var) args <- args[names(args) != "T"]
if(any(arg.is.P) & !P.is.var) args <- args[names(args) != "P"]
if(any(arg.is.IS) & !IS.is.var) args <- args[names(args) != "IS"]
# The property we're interested in
if("what" %in% names(args)) {
what <- args[[names(args) == "what"]]
args <- args[names(args) != "what"]
}
# Assemble the variables
if(length(args) > 0) {
for(i in 1:length(args)) {
nametxt <- names(args)[i]
if(transect) lims.orig <- c(min(args[[i]]), max(args[[i]]))
else lims.orig <- args[[i]][1:2]
if(names(args)[i] == "pH") {
names(args)[i] <- "H+"
if(transect) args[[i]] <- -args[[i]]
else args[[i]][1:2] <- -args[[i]][1:2]
}
if(names(args)[i] == "pe") {
names(args)[i] <- "e-"
if(transect) args[[i]] <- -args[[i]]
else args[[i]][1:2] <- -args[[i]][1:2]
}
if(length(args[[i]]) < 3 & !transect) args[[i]] <- c(args[[i]], res)
vars[length(vars)+1] <- names(args)[i]
if(transect) {
vals[[length(vars)]] <- args[[i]]
lims[[length(vars)]] <- c(lims.orig, length(vals[[i]]))
} else {
vals[[length(vars)]] <- seq(args[[i]][1], args[[i]][2], length.out = args[[i]][3])
lims[[length(vars)]] <- args[[i]]
}
names(lims)[length(vars)] <- names(args)[i]
# Say something about the identities, ranges, and units of the variables
unittxt <- ""
# Number of values
if(transect) n <- length(args[[i]]) else n <- args[[i]][3]
# Physical state
ibasis <- match(nametxt, rownames(thermo$basis))
if(isTRUE(as.logical(ibasis))) {
if(thermo$basis$state[ibasis] == "gas") nametxt <- paste("log10(f_", nametxt, ")", sep = "")
else nametxt <- paste("log10(a_", nametxt, ")", sep = "")
} else {
# Stop if the argument doesn't correspond to a basis species, T, P, or IS
if(!nametxt %in% c("T", "P", "IS")) {
if(! (nametxt == "pH" & 'H+' %in% rownames(thermo$basis) | nametxt %in% c("pe", "Eh") & 'e-' %in% rownames(thermo$basis))) {
stop(nametxt, " is not one of T, P, or IS, and does not match any basis species")
}
}
}
# Temperature and pressure and Eh
if(nametxt == "T") unittxt <- " K"
if(nametxt == "P") unittxt <- " bar"
if(nametxt == "Eh") unittxt <- " V"
message("affinity: variable ", length(vars), " is ", nametxt,
" at ", n, " values from ", lims.orig[1], " to ", lims.orig[2], unittxt)
}
}
args <- list(what = what, vars = vars, vals = vals, lims = lims, T = T, P = P, IS = IS, transect = transect)
# Convert Eh to pe
if("Eh" %in% args$vars) {
# Get Eh into our dimensions
Eh.args <- args
# What variable is Eh
Eh.var <- which(args$vars == "Eh")
Eh.args$what <- args$vals[[Eh.var]]
Eh.args$sout$out <- Eh.var
Eh <- do.call("energy", Eh.args)
# Get temperature into our dimensions
T.args <- args
if("T" %in% args$vars) {
T.var <- which(args$vars == "T")
T.args$what <- args$vals[[T.var]]
} else {
T.var <- 1
T.args$what <- T
}
T.args$sout$out <- T.var
T <- do.call("energy", T.args)
# Do the conversion on vectors
mydim <- dim(Eh)
Eh <- as.vector(Eh)
T <- as.vector(T)
pe <- convert(Eh, "pe", T=T)
dim(pe) <- mydim
# Update the arguments list
args$vars[Eh.var] <- "e-"
args$vals[[Eh.var]] <- -pe
}
return(args)
}
A.ionization <- function(iprotein, vars, vals, T = 298.15, P = "Psat", pH = 7, transect = FALSE) {
# A function to build a list of values of A/2.303RT of protein ionization
# that can be used by energy(); 20120527 jmd
# Some of the variables might not affect the values (e.g. logfO2)
# What are the variables that affect the values
T <- convert(T, "C")
if(!is.na(iT <- match("T", vars))) T <- convert(vals[[iT]], "C")
if(!is.na(iP <- match("P", vars))) P <- vals[[iP]]
if(!is.na(iHplus <- match("H+", vars))) pH <- -vals[[iHplus]]
# Is it a transect (single points) or a grid?
if(transect) TPpH <- list(T = T, P = P, pH = pH)
else {
# Make a grid of all combinations
# Put T, P, pH in same order as they show up vars
egargs <- list(T = T, P = P, pH = pH)
# order(c(NA, NA, NA)) might segfault in some versions of R (seen in R 2.15.3 on Linux)
if(is.na(iT) & is.na(iP) & is.na(iHplus)) TPpHorder <- c(1, 2, 3)
else TPpHorder <- order(c(iT, iP, iHplus))
egargs <- c(egargs[TPpHorder], list(stringsAsFactors = FALSE))
TPpH <- do.call(expand.grid, egargs)
# Figure out the dimensions of T-P-pH (making sure to drop any that aren't in vars)
TPpHdim <- numeric(3)
TPpHdim[iT] <- length(T)
TPpHdim[iP] <- length(P)
TPpHdim[iHplus] <- length(pH)
TPpHdim <- TPpHdim[!TPpHdim == 0]
# Figure out the dimensions of the other vars
othervars <- vars[!vars %in% c("T", "P", "H+")]
iother <- match(othervars, vars)
otherdim <- sapply(vals, length)[iother]
# If Eh was given to energy.args, values of pe were calculated in all dimensions
# Figure out the original length of the Eh variable
ieminus <- match("e-", vars[iother])
if(!is.na(ieminus)) {
otherdim[ieminus] <- NA
edim <- dim(vals[[iother[ieminus]]])
# Loop TPpHdim and otherdim, taking out each one from edim
for(dim in c(TPpHdim, otherdim)) {
id <- match(dim, edim)
edim[id] <- NA
}
otherdim[ieminus] <- edim[!is.na(edim)]
}
# The permutation vector
if(length(iother) > 0) allvars <- c(vars[-iother], vars[iother])
else allvars <- vars
perm <- match(vars, allvars)
}
# Initialize output list
out <- vector("list", length(iprotein))
# Get aa from iprotein
aa <- pinfo(iprotein)
# Calculate the values of A/2.303RT as a function of T-P-pH
A <- ionize.aa(aa = aa, property = "A", T = TPpH$T, P = TPpH$P, pH = TPpH$pH)
if(transect) {
# Just turn the values into a list
for(i in 1:length(iprotein)) out[[i]] <- A[, i]
} else for(i in 1:length(iprotein)) {
# What are the values of ionization affinity for this protein
thisA <- A[, i]
# Apply the dimensions of T-P-pH
tpphdim <- TPpHdim
if(length(tpphdim) == 0) tpphdim <- 1
thisA <- array(thisA, tpphdim)
# Grow into the dimensions of all vars
alldim <- c(TPpHdim, otherdim)
if(length(alldim) == 0) alldim <- 1
thisA <- array(thisA, alldim)
# Now permute the array to put dimensions in same order as the variables
thisA <- aperm(thisA, perm)
# Store in output list
out[[i]] <- thisA
}
return(out)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.