Title: | Automated C Code Generation for 'deSolve', 'bvpSolve' |
---|---|
Description: | Generates all necessary C functions allowing the user to work with the compiled-code interface of ode() and bvptwp(). The implementation supports "forcings" and "events". Also provides functions to symbolically compute Jacobians, sensitivity equations and adjoint sensitivities being the basis for sensitivity analysis. |
Authors: | Daniel Kaschek |
Maintainer: | Daniel Kaschek <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.1.1 |
Built: | 2024-11-25 05:24:08 UTC |
Source: | https://github.com/dkaschek/code |
Generates all necessary C functions allowing the user to work with the compiled-code interface of ode() and bvptwp(). The implementation supports "forcings" and "events". The package also provides functions to symbolically compute Jacobians, sensitivity equations and adjoint sensitivities being the basis for sensitivity analysis.
Package: | cOde |
Type: | Package |
Version: | 0.2 |
Date: | 2015-03-05 |
License: | GNU Public License 2 or above |
The system of ordinary differential equations is defined as a named character vector. This character vector is translated in C syntax and the code output format is matched to the requirements of deSolve and bvpSolve.
Daniel Kaschek <[email protected]>
Compute adjoint equations of a function symbolically
adjointSymb(f, states = names(f), parameters = NULL, inputs = NULL)
adjointSymb(f, states = names(f), parameters = NULL, inputs = NULL)
f |
Named vector of type character, the functions |
states |
Character vector of the ODE states for which observations are available |
parameters |
Character vector of the parameters |
inputs |
Character vector of the "variable" input states, i.e. time-dependent parameters (in contrast to the forcings). |
The adjoint equations are computed with respect to the functional
where x are the states being constrained
by the ODE, u are the inputs and xD and uD indicate the trajectories to be best
possibly approached. When the ODE is linear with respect to u, the attribute inputs
of the returned equations can be used to replace all occurences of u by the corresponding
character in the attribute. This guarantees that the input course is optimal with
respect to the above function.
Named vector of type character with the adjoint equations. The vector has attributes "chi" (integrand of the chisquare functional), "grad" (integrand of the gradient of the chisquare functional), "forcings" (character vector of the forcings necessary for integration of the adjoint equations) and "inputs" (the input expressed as a function of the adjoint variables).
## Not run: ###################################################################### ## Solve an optimal control problem: ###################################################################### library(bvpSolve) # O2 + O <-> O3 # O3 is removed by a variable rate u(t) f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3 - u * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute adjoints equations and replace u by optimal input f_a <- adjointSymb(f, states = c("O3"), inputs = "u") inputs <- attr(f_a, "inputs") f_tot <- replaceSymbols("u", inputs, c(f, f_a)) forcings <- attr(f_a, "forcings") # Initialize times, states, parameters times <- seq(0, 15, by = .1) boundary <- data.frame( name = c("O3", "O2", "O", "adjO3", "adjO2", "adjO"), yini = c(0.5, 2, 2.5, NA, NA, NA), yend = c(NA, NA, NA, 0, 0, 0)) pars <- c(build_O3 = .2, decay_O3 = .1, eps = 1) # Generate ODE function func <- funC(f = f_tot, forcings = forcings, jacobian = "full", boundary = boundary, modelname = "example5") # Initialize forcings (the objective) forcData <- data.frame(time = times, name = rep(forcings, each=length(times)), value = rep( c(0.5, 0, 1, 1), each=length(times))) forc <- setForcings(func, forcData) # Solve BVP out <- bvptwpC(x = times, func = func, parms = pars, forcings = forc) # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:4] M2 <- with(list(uD = 0, O3 = out[,2], adjO3 = out[,5], eps = 1, weightuD = 1), eval(parse(text=inputs))) matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") abline(h = .5, lty=2) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1, xlab="time", ylab="value", main="input u") abline(h = 0, lty=2) ## End(Not run)
## Not run: ###################################################################### ## Solve an optimal control problem: ###################################################################### library(bvpSolve) # O2 + O <-> O3 # O3 is removed by a variable rate u(t) f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3 - u * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute adjoints equations and replace u by optimal input f_a <- adjointSymb(f, states = c("O3"), inputs = "u") inputs <- attr(f_a, "inputs") f_tot <- replaceSymbols("u", inputs, c(f, f_a)) forcings <- attr(f_a, "forcings") # Initialize times, states, parameters times <- seq(0, 15, by = .1) boundary <- data.frame( name = c("O3", "O2", "O", "adjO3", "adjO2", "adjO"), yini = c(0.5, 2, 2.5, NA, NA, NA), yend = c(NA, NA, NA, 0, 0, 0)) pars <- c(build_O3 = .2, decay_O3 = .1, eps = 1) # Generate ODE function func <- funC(f = f_tot, forcings = forcings, jacobian = "full", boundary = boundary, modelname = "example5") # Initialize forcings (the objective) forcData <- data.frame(time = times, name = rep(forcings, each=length(times)), value = rep( c(0.5, 0, 1, 1), each=length(times))) forc <- setForcings(func, forcData) # Solve BVP out <- bvptwpC(x = times, func = func, parms = pars, forcings = forc) # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:4] M2 <- with(list(uD = 0, O3 = out[,2], adjO3 = out[,5], eps = 1, weightuD = 1), eval(parse(text=inputs))) matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") abline(h = .5, lty=2) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1, xlab="time", ylab="value", main="input u") abline(h = 0, lty=2) ## End(Not run)
Interface to bvptwp()
bvptwpC( yini = NULL, x, func, yend = NULL, parms, xguess = NULL, yguess = NULL, ... )
bvptwpC( yini = NULL, x, func, yend = NULL, parms, xguess = NULL, yguess = NULL, ... )
yini |
named vector of type numeric. Initial values to be overwritten. |
x |
vector of type numeric. Integration times |
func |
return value from funC() with a boundary argument. |
yend |
named vector of type numeric. End values to be overwritten. |
parms |
named vector of type numeric. The dynamic parameters. |
xguess |
vector of type numeric, the x values |
yguess |
matrix with as many rows as variables and columns as x values |
... |
further arguments going to |
See bvpSolve-package for a full description of possible arguments
matrix with times and states
## Not run: ###################################################################### ## Boundary value problem: Ozon formation with fixed ozon/oxygen ratio ## at final time point ###################################################################### library(bvpSolve) # O2 + O <-> O3 # diff = O2 - O3 # build_O3 = const. f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3", diff = "-2 * build_O3 * O2 * O + 2 * decay_O3 * O3", build_O3 = "0" ) bound <- data.frame( name = names(f), yini = c(0, 3, 2, 3, NA), yend = c(NA, NA, NA, 0, NA) ) # Generate ODE function func <- funC(f, jacobian="full", boundary = bound, modelname = "example4") # Initialize times, states, parameters and forcings times <- seq(0, 15, by = .1) pars <- c(decay_O3 = .1) xguess <- times yguess <- matrix(c(1, 1, 1, 1, 1), ncol=length(times), nrow = length(f)) # Solve BVP out <- bvptwpC(x = times, func = func, parms = pars, xguess = xguess, yguess = yguess) # Solve BVP for different ini values, end values and parameters yini <- c(O3 = 2) yend <- c(diff = 0.2) pars <- c(decay_O3 = .01) out <- bvptwpC(yini = yini, yend = yend, x = times, func = func, parms = pars, xguess = xguess, yguess = yguess) # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:5] M2 <- cbind(out[,6], pars) matplot(t, M1, type="l", lty=1, col=1:4, xlab="time", ylab="value", main="states") legend("topright", legend = c("O3", "O2", "O", "O2 - O3"), lty=1, col=1:4) matplot(t, M2, type="l", lty=1, col=1:2, xlab="time", ylab="value", main="parameters") legend("right", legend = c("build_O3", "decay_O3"), lty=1, col=1:2) ## End(Not run)
## Not run: ###################################################################### ## Boundary value problem: Ozon formation with fixed ozon/oxygen ratio ## at final time point ###################################################################### library(bvpSolve) # O2 + O <-> O3 # diff = O2 - O3 # build_O3 = const. f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3", diff = "-2 * build_O3 * O2 * O + 2 * decay_O3 * O3", build_O3 = "0" ) bound <- data.frame( name = names(f), yini = c(0, 3, 2, 3, NA), yend = c(NA, NA, NA, 0, NA) ) # Generate ODE function func <- funC(f, jacobian="full", boundary = bound, modelname = "example4") # Initialize times, states, parameters and forcings times <- seq(0, 15, by = .1) pars <- c(decay_O3 = .1) xguess <- times yguess <- matrix(c(1, 1, 1, 1, 1), ncol=length(times), nrow = length(f)) # Solve BVP out <- bvptwpC(x = times, func = func, parms = pars, xguess = xguess, yguess = yguess) # Solve BVP for different ini values, end values and parameters yini <- c(O3 = 2) yend <- c(diff = 0.2) pars <- c(decay_O3 = .01) out <- bvptwpC(yini = yini, yend = yend, x = times, func = func, parms = pars, xguess = xguess, yguess = yguess) # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:5] M2 <- cbind(out[,6], pars) matplot(t, M1, type="l", lty=1, col=1:4, xlab="time", ylab="value", main="states") legend("topright", legend = c("O3", "O2", "O", "O2 - O3"), lty=1, col=1:4) matplot(t, M2, type="l", lty=1, col=1:2, xlab="time", ylab="value", main="parameters") legend("right", legend = c("build_O3", "decay_O3"), lty=1, col=1:2) ## End(Not run)
Compile and load shared object implementing the ODE system.
compileAndLoad(filename, dllname, fcontrol, verbose)
compileAndLoad(filename, dllname, fcontrol, verbose)
filename |
Full file name of the source file. |
dllname |
Base name for source and dll file. |
fcontrol |
Interpolation method for forcings. |
verbose |
Print compiler output or not. |
Daniel Kaschek, [email protected]
Wolfgang Mader, [email protected]
Generate C code for a function and compile it
funC( f, forcings = NULL, events = NULL, fixed = NULL, outputs = NULL, jacobian = c("none", "full", "inz.lsodes", "jacvec.lsodes"), rootfunc = NULL, boundary = NULL, compile = TRUE, fcontrol = c("nospline", "einspline"), nGridpoints = -1, includeTimeZero = TRUE, precision = 1e-05, modelname = NULL, verbose = FALSE, solver = c("deSolve", "Sundials") )
funC( f, forcings = NULL, events = NULL, fixed = NULL, outputs = NULL, jacobian = c("none", "full", "inz.lsodes", "jacvec.lsodes"), rootfunc = NULL, boundary = NULL, compile = TRUE, fcontrol = c("nospline", "einspline"), nGridpoints = -1, includeTimeZero = TRUE, precision = 1e-05, modelname = NULL, verbose = FALSE, solver = c("deSolve", "Sundials") )
f |
Named character vector containing the right-hand sides of the ODE.
You may use the key word |
forcings |
Character vector with the names of the forcings |
events |
data.frame of events with columns "var" (character, the name of the state to be
affected), "time" (numeric or character, time point),
"value" (numeric or character, value), "method" (character, either
"replace" or "add"). See events. If "var" and "time" are
characters, their values need to be speciefied in the parameter vector
when calling |
fixed |
character vector with the names of parameters (initial values and dynamic) for which no sensitivities are required (will speed up the integration). |
outputs |
Named character vector for additional output variables, see
arguments |
jacobian |
Character, either "none" (no jacobian is computed), "full" (full jacobian is computed and written as a function into the C file) or "inz.lsodes" (only the non-zero elements of the jacobian are determined, see lsodes) |
rootfunc |
Named character vector. The root function (see
lsoda). Besides the variable names ( |
boundary |
data.frame with columns name, yini, yend specifying the boundary condition set-up. NULL if not a boundary value problem |
compile |
Logical. If FALSE, only the C file is written |
fcontrol |
Character, either |
nGridpoints |
Integer, defining for which time points the ODE is evaluated
or the solution is returned: Set |
includeTimeZero |
Logical. Include t = 0 in the integration time points if |
precision |
Numeric. Only used when |
modelname |
Character. The C file is generated in the working directory
and is named <modelname>.c. If |
verbose |
Print compiler output to R command line. |
solver |
Select the solver suite as either |
The function replaces variables by arrays y[i]
, etc. and
replaces "^" by pow() in order to have the correct C syntax. The file name
of the C-File is derived from f
. I.e. funC(abc, ...
will
generate a file abc.c in the current directory. Currently, only explicit
ODE specification is supported, i.e. you need to have the right-hand sides
of the ODE.
the name of the generated shared object file together with a number of attributes
## Not run: # Exponential decay plus constant supply f <- c(x = "-k*x + supply") func <- funC(f, forcings = "supply") # Example 2: root function f <- c(A = "-k1*A + k2*B", B = "k1*A - k2*B") rootfunc <- c(steadyState = "-k1*A + k2*B - tol") func <- funC(f, rootfunc = rootfunc, modelname = "test") yini <- c(A = 1, B = 2) parms <- c(k1 = 1, k2 = 5, tol = 0.1) times <- seq(0, 10, len = 100) odeC(yini, times, func, parms) ## End(Not run)
## Not run: # Exponential decay plus constant supply f <- c(x = "-k*x + supply") func <- funC(f, forcings = "supply") # Example 2: root function f <- c(A = "-k1*A + k2*B", B = "k1*A - k2*B") rootfunc <- c(steadyState = "-k1*A + k2*B - tol") func <- funC(f, rootfunc = rootfunc, modelname = "test") yini <- c(A = 1, B = 2) parms <- c(k1 = 1, k2 = 5, tol = 0.1) times <- seq(0, 10, len = 100) odeC(yini, times, func, parms) ## End(Not run)
Get symbols from a character
getSymbols(char, exclude = NULL)
getSymbols(char, exclude = NULL)
char |
Character vector (e.g. equation) |
exclude |
Character vector, the symbols to be excluded from the return value |
character vector with the symbols
getSymbols(c("A*AB+B^2"))
getSymbols(c("A*AB+B^2"))
Compute Jacobian of a function symbolically
jacobianSymb(f, variables = NULL)
jacobianSymb(f, variables = NULL)
f |
named vector of type character, the functions |
variables |
other variables, e.g. paramters, f depends on. If variables is
given, f is derived with respect to variables instead of |
named vector of type character with the symbolic derivatives
jacobianSymb(c(A="A*B", B="A+B")) jacobianSymb(c(x="A*B", y="A+B"), c("A", "B"))
jacobianSymb(c(A="A*B", B="A+B")) jacobianSymb(c(x="A*B", y="A+B"), c("A", "B"))
Interface to ode()
odeC(y, times, func, parms, ...)
odeC(y, times, func, parms, ...)
y |
named vector of type numeric. Initial values for the integration |
times |
vector of type numeric. Integration times. If |
func |
return value from funC() |
parms |
named vector of type numeric. |
... |
further arguments going to |
See deSolve-package for a full description of possible arguments
matrix with times and states
## Not run: ###################################################################### ## Ozone formation and decay, modified by external forcings ###################################################################### library(deSolve) data(forcData) forcData$value <- forcData$value + 1 # O2 + O <-> O3 f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3" ) # Generate ODE function forcings <- c("u_build", "u_degrade") func <- funC(f, forcings = forcings, modelname = "test", fcontrol = "nospline", nGridpoints = 10) # Initialize times, states, parameters and forcings times <- seq(0, 8, by = .1) yini <- c(O3 = 0, O2 = 3, O = 2) pars <- c(build_O3 = 1/6, decay_O3 = 1) forc <- setForcings(func, forcData) # Solve ODE out <- odeC(y = yini, times = times, func = func, parms = pars, forcings = forc) # Plot solution par(mfcol=c(1,2)) t1 <- unique(forcData[,2]) M1 <- matrix(forcData[,3], ncol=2) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t1, M1, type="l", lty=1, col=1:2, xlab="time", ylab="value", main="forcings", ylim=c(0, 4)) matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="forcings", add=TRUE) legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ###################################################################### ## Ozone formation and decay, modified by events ###################################################################### f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", u_build = "0", # piecewise constant u_degrade = "0" # piecewise constant ) # Define parametric events events.pars <- data.frame( var = c("u_degrade", "u_degrade", "u_build"), time = c("t_on", "t_off", "2"), value = c("plus", "minus", "2"), method = "replace" ) # Declar parameteric events when generating funC object func <- funC(f, forcings = NULL, events = events.pars, modelname = "test", fcontrol = "nospline", nGridpoints = -1) # Set Parameters yini <- c(O3 = 0, O2 = 3, O = 2, u_build = 1, u_degrade = 1) times <- seq(0, 8, by = .1) pars <- c(build_O3 = 1/6, decay_O3 = 1, t_on = exp(rnorm(1, 0)), t_off = 6, plus = 3, minus = 1) # Solve ODE with additional fixed-value events out <- odeC(y = yini, times = times, func = func, parms = pars) # Plot solution par(mfcol=c(1,2)) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="events") legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ###################################################################### ## Ozone formation and decay, modified by events triggered by root ###################################################################### f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", u_build = "0", # piecewise constant u_degrade = "0" # piecewise constant ) # Define parametric events events.pars <- data.frame( var = c("u_degrade", "u_degrade", "u_build", "O3"), time = c("t_on", "t_off", "2", "t_thres_O3"), value = c("plus", "minus", "2", "0"), root = c(NA, NA, NA, "O3 - thres_O3"), method = "replace" ) # Declar parameteric events when generating funC object func <- funC(f, forcings = NULL, events = events.pars, modelname = "test", fcontrol = "nospline", nGridpoints = -1) # Set Parameters yini <- c(O3 = 0, O2 = 3, O = 2, u_build = 1, u_degrade = 1) times <- seq(0, 8, by = .01) pars <- c(build_O3 = 1/6, decay_O3 = 1, t_on = exp(rnorm(1, 0)), t_off = 6, plus = 3, minus = 1, thres_O3 = 0.5, t_thres_O3 = 1) # Solve ODE with additional fixed-value events out <- odeC(y = yini, times = times, func = func, parms = pars, method = "lsode") class(out) <- c("deSolve") plot(out) # Plot solution par(mfcol=c(1,2)) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="events") legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ## End(Not run)
## Not run: ###################################################################### ## Ozone formation and decay, modified by external forcings ###################################################################### library(deSolve) data(forcData) forcData$value <- forcData$value + 1 # O2 + O <-> O3 f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3" ) # Generate ODE function forcings <- c("u_build", "u_degrade") func <- funC(f, forcings = forcings, modelname = "test", fcontrol = "nospline", nGridpoints = 10) # Initialize times, states, parameters and forcings times <- seq(0, 8, by = .1) yini <- c(O3 = 0, O2 = 3, O = 2) pars <- c(build_O3 = 1/6, decay_O3 = 1) forc <- setForcings(func, forcData) # Solve ODE out <- odeC(y = yini, times = times, func = func, parms = pars, forcings = forc) # Plot solution par(mfcol=c(1,2)) t1 <- unique(forcData[,2]) M1 <- matrix(forcData[,3], ncol=2) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t1, M1, type="l", lty=1, col=1:2, xlab="time", ylab="value", main="forcings", ylim=c(0, 4)) matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="forcings", add=TRUE) legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ###################################################################### ## Ozone formation and decay, modified by events ###################################################################### f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", u_build = "0", # piecewise constant u_degrade = "0" # piecewise constant ) # Define parametric events events.pars <- data.frame( var = c("u_degrade", "u_degrade", "u_build"), time = c("t_on", "t_off", "2"), value = c("plus", "minus", "2"), method = "replace" ) # Declar parameteric events when generating funC object func <- funC(f, forcings = NULL, events = events.pars, modelname = "test", fcontrol = "nospline", nGridpoints = -1) # Set Parameters yini <- c(O3 = 0, O2 = 3, O = 2, u_build = 1, u_degrade = 1) times <- seq(0, 8, by = .1) pars <- c(build_O3 = 1/6, decay_O3 = 1, t_on = exp(rnorm(1, 0)), t_off = 6, plus = 3, minus = 1) # Solve ODE with additional fixed-value events out <- odeC(y = yini, times = times, func = func, parms = pars) # Plot solution par(mfcol=c(1,2)) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="events") legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ###################################################################### ## Ozone formation and decay, modified by events triggered by root ###################################################################### f <- c( O3 = " (build_O3 + u_build) * O2 * O - (decay_O3 + u_degrade) * O3", O2 = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", O = "-(build_O3 + u_build) * O2 * O + (decay_O3 + u_degrade) * O3", u_build = "0", # piecewise constant u_degrade = "0" # piecewise constant ) # Define parametric events events.pars <- data.frame( var = c("u_degrade", "u_degrade", "u_build", "O3"), time = c("t_on", "t_off", "2", "t_thres_O3"), value = c("plus", "minus", "2", "0"), root = c(NA, NA, NA, "O3 - thres_O3"), method = "replace" ) # Declar parameteric events when generating funC object func <- funC(f, forcings = NULL, events = events.pars, modelname = "test", fcontrol = "nospline", nGridpoints = -1) # Set Parameters yini <- c(O3 = 0, O2 = 3, O = 2, u_build = 1, u_degrade = 1) times <- seq(0, 8, by = .01) pars <- c(build_O3 = 1/6, decay_O3 = 1, t_on = exp(rnorm(1, 0)), t_off = 6, plus = 3, minus = 1, thres_O3 = 0.5, t_thres_O3 = 1) # Solve ODE with additional fixed-value events out <- odeC(y = yini, times = times, func = func, parms = pars, method = "lsode") class(out) <- c("deSolve") plot(out) # Plot solution par(mfcol=c(1,2)) t2 <- out[,1] M2 <- out[,2:4] M3 <- out[,5:6] matplot(t2, M3, type="l", lty=2, col=1:2, xlab="time", ylab="value", main="events") legend("topleft", legend = c("u_build", "u_degrade"), lty=1, col=1:2) matplot(t2, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="response") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) ## End(Not run)
Compute matrix product symbolically
prodSymb(M, N)
prodSymb(M, N)
M |
matrix of type character |
N |
matrix of type character |
Matrix of type character, the matrix product of M and N
reduceSensitivities
reduceSensitivities(sens, vanishing)
reduceSensitivities(sens, vanishing)
sens |
Named character, the sensitivity equations |
vanishing |
Character, names of the vanishing sensitivities |
Given the set vanishing
of vanishing sensitivities, the algorithm
determins sensitivities that vanish as a consequence of the first set.
Named character, the sensitivity equations with zero entries for vanishing sensitivities.
Replace integer number in a character vector by other double
replaceNumbers(x)
replaceNumbers(x)
x |
vector of type character, the object where the replacement should take place |
vector of type character, conserves the names of x.
Replace a binary operator in a string by a function
replaceOperation(what, by, x)
replaceOperation(what, by, x)
what |
character, the operator symbol, e.g. "^" |
by |
character, the function string, e.g. "pow" |
x |
vector of type character, the object where the replacement should take place |
vector of type character
replaceOperation("^", "pow", "(x^2 + y^2)^.5")
replaceOperation("^", "pow", "(x^2 + y^2)^.5")
Replace symbols in a character vector by other symbols
replaceSymbols(what, by, x)
replaceSymbols(what, by, x)
what |
vector of type character, the symbols to be replaced, e.g. c("A", "B") |
by |
vector of type character, the replacement, e.g. c("x[0]", "x[1]") |
x |
vector of type character, the object where the replacement should take place |
vector of type character, conserves the names of x.
replaceSymbols(c("A", "B"), c("x[0]", "x[1]"), c("A*B", "A+B+C"))
replaceSymbols(c("A", "B"), c("x[0]", "x[1]"), c("A*B", "A+B+C"))
Compute sensitivity equations of a function symbolically
sensitivitiesSymb( f, states = names(f), parameters = NULL, inputs = NULL, events = NULL, reduce = FALSE )
sensitivitiesSymb( f, states = names(f), parameters = NULL, inputs = NULL, events = NULL, reduce = FALSE )
f |
named vector of type character, the functions |
states |
Character vector. Sensitivities are computed with respect to initial values of these states |
parameters |
Character vector. Sensitivities are computed with respect to initial values of these parameters |
inputs |
Character vector. Input functions or forcings. They are excluded from the computation of sensitivities. |
events |
data.frame of events with columns "var" (character, the name of the state to be
affected), "time" (numeric or character, time point),
"value" (numeric or character, value), "method" (character, either
"replace" or "add"). See events.
Within |
reduce |
Logical. Attempts to determine vanishing sensitivities, removes their equations and replaces their right-hand side occurences by 0. |
The sensitivity equations are ODEs that are derived from the original ODE f. They describe the sensitivity of the solution curve with respect to parameters like initial values and other parameters contained in f. These equtions are also useful for parameter estimation by the maximum-likelihood method. For consistency with the time-continuous setting provided by adjointSymb, the returned equations contain attributes for the chisquare functional and its gradient.
Named vector of type character with the sensitivity equations. Furthermore,
attributes "chi" (the integrand of the chisquare functional), "grad" (the integrand
of the gradient of the chisquare functional), "forcings" (Character vector of the
additional forcings being necessare to compute chi
and grad
) and "yini" (
The initial values of the sensitivity equations) are returned.
## Not run: ###################################################################### ## Sensitivity analysis of ozone formation ###################################################################### library(deSolve) # O2 + O <-> O3 f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute sensitivity equations f_s <- sensitivitiesSymb(f) # Generate ODE function func <- funC(c(f, f_s)) # Initialize times, states, parameters and forcings times <- seq(0, 15, by = .1) yini <- c(O3 = 0, O2 = 3, O = 2, attr(f_s, "yini")) pars <- c(build_O3 = .1, decay_O3 = .01) # Solve ODE out <- odeC(y = yini, times = times, func = func, parms = pars) # Plot solution par(mfcol=c(2,3)) t <- out[,1] M1 <- out[,2:4] M2 <- out[,5:7] M3 <- out[,8:10] M4 <- out[,11:13] M5 <- out[,14:16] M6 <- out[,17:19] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="solution") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O3)") matplot(t, M3, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O2)") matplot(t, M4, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O)") matplot(t, M5, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d build_O3)") matplot(t, M6, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d decay_O3)") ## End(Not run) ## Not run: ###################################################################### ## Estimate parameter values from experimental data ###################################################################### library(deSolve) # O2 + O <-> O3 # diff = O2 - O3 # build_O3 = const. f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute sensitivity equations and get attributes f_s <- sensitivitiesSymb(f) chi <- attr(f_s, "chi") grad <- attr(f_s, "grad") forcings <- attr(f_s, "forcings") # Generate ODE function func <- funC(f = c(f, f_s, chi, grad), forcings = forcings, fcontrol = "nospline", modelname = "example3") # Initialize times, states, parameters times <- seq(0, 15, by = .1) yini <- c(O3 = 0, O2 = 2, O = 2.5) yini_s <- attr(f_s, "yini") yini_chi <- c(chi = 0) yini_grad <- rep(0, length(grad)); names(yini_grad) <- names(grad) pars <- c(build_O3 = .2, decay_O3 = .1) # Initialize forcings (the data) data(oxygenData) forcData <- data.frame(time = oxygenData[,1], name = rep( colnames(oxygenData[,-1]), each=dim(oxygenData)[1]), value = as.vector(oxygenData[,-1])) forc <- setForcings(func, forcData) # Solve ODE out <- odeC(y = c(yini, yini_s, yini_chi, yini_grad), times = times, func = func, parms = pars, forcings = forc, method = "lsodes") # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:4] M2 <- out[,names(grad)] tD <- oxygenData[,1] M1D <- oxygenData[,2:4] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") matplot(tD, M1D, type="b", lty=2, col=1:3, pch=4, add=TRUE) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:5, xlab="time", ylab="value", main="gradient") legend("topleft", legend = names(grad), lty=1, col=1:5) # Define objective function obj <- function(p) { out <- odeC(y = c(p[names(f)], yini_s, yini_chi, yini_grad), times = times, func = func, parms = p[names(pars)], forcings = forc, method="lsodes") value <- as.vector(tail(out, 1)[,"chi"]) gradient <- as.vector( tail(out, 1)[,paste("chi", names(p), sep=".")]) hessian <- gradient%*%t(gradient) return(list(value = value, gradient = gradient, hessian = hessian)) } # Fit the data myfit <- optim(par = c(yini, pars), fn = function(p) obj(p)$value, gr = function(p) obj(p)$gradient, method = "L-BFGS-B", lower=0, upper=5) # Model prediction for fit parameters prediction <- odeC(y = c(myfit$par[1:3], yini_s, yini_chi, yini_grad), times = times, func = func, parms = myfit$par[4:5], forcings = forc, method = "lsodes") # Plot solution par(mfcol=c(1,2)) t <- prediction[,1] M1 <- prediction[,2:4] M2 <- prediction[,names(grad)] tD <- oxygenData[,1] M1D <- oxygenData[,2:4] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") matplot(tD, M1D, type="b", lty=2, col=1:3, pch=4, add=TRUE) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:5, xlab="time", ylab="value", main="gradient") legend("topleft", legend = names(grad), lty=1, col=1:5) ## End(Not run)
## Not run: ###################################################################### ## Sensitivity analysis of ozone formation ###################################################################### library(deSolve) # O2 + O <-> O3 f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute sensitivity equations f_s <- sensitivitiesSymb(f) # Generate ODE function func <- funC(c(f, f_s)) # Initialize times, states, parameters and forcings times <- seq(0, 15, by = .1) yini <- c(O3 = 0, O2 = 3, O = 2, attr(f_s, "yini")) pars <- c(build_O3 = .1, decay_O3 = .01) # Solve ODE out <- odeC(y = yini, times = times, func = func, parms = pars) # Plot solution par(mfcol=c(2,3)) t <- out[,1] M1 <- out[,2:4] M2 <- out[,5:7] M3 <- out[,8:10] M4 <- out[,11:13] M5 <- out[,14:16] M6 <- out[,17:19] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="solution") legend("topright", legend = c("O3", "O2", "O"), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O3)") matplot(t, M3, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O2)") matplot(t, M4, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d O)") matplot(t, M5, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d build_O3)") matplot(t, M6, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="d/(d decay_O3)") ## End(Not run) ## Not run: ###################################################################### ## Estimate parameter values from experimental data ###################################################################### library(deSolve) # O2 + O <-> O3 # diff = O2 - O3 # build_O3 = const. f <- c( O3 = " build_O3 * O2 * O - decay_O3 * O3", O2 = "-build_O3 * O2 * O + decay_O3 * O3", O = "-build_O3 * O2 * O + decay_O3 * O3" ) # Compute sensitivity equations and get attributes f_s <- sensitivitiesSymb(f) chi <- attr(f_s, "chi") grad <- attr(f_s, "grad") forcings <- attr(f_s, "forcings") # Generate ODE function func <- funC(f = c(f, f_s, chi, grad), forcings = forcings, fcontrol = "nospline", modelname = "example3") # Initialize times, states, parameters times <- seq(0, 15, by = .1) yini <- c(O3 = 0, O2 = 2, O = 2.5) yini_s <- attr(f_s, "yini") yini_chi <- c(chi = 0) yini_grad <- rep(0, length(grad)); names(yini_grad) <- names(grad) pars <- c(build_O3 = .2, decay_O3 = .1) # Initialize forcings (the data) data(oxygenData) forcData <- data.frame(time = oxygenData[,1], name = rep( colnames(oxygenData[,-1]), each=dim(oxygenData)[1]), value = as.vector(oxygenData[,-1])) forc <- setForcings(func, forcData) # Solve ODE out <- odeC(y = c(yini, yini_s, yini_chi, yini_grad), times = times, func = func, parms = pars, forcings = forc, method = "lsodes") # Plot solution par(mfcol=c(1,2)) t <- out[,1] M1 <- out[,2:4] M2 <- out[,names(grad)] tD <- oxygenData[,1] M1D <- oxygenData[,2:4] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") matplot(tD, M1D, type="b", lty=2, col=1:3, pch=4, add=TRUE) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:5, xlab="time", ylab="value", main="gradient") legend("topleft", legend = names(grad), lty=1, col=1:5) # Define objective function obj <- function(p) { out <- odeC(y = c(p[names(f)], yini_s, yini_chi, yini_grad), times = times, func = func, parms = p[names(pars)], forcings = forc, method="lsodes") value <- as.vector(tail(out, 1)[,"chi"]) gradient <- as.vector( tail(out, 1)[,paste("chi", names(p), sep=".")]) hessian <- gradient%*%t(gradient) return(list(value = value, gradient = gradient, hessian = hessian)) } # Fit the data myfit <- optim(par = c(yini, pars), fn = function(p) obj(p)$value, gr = function(p) obj(p)$gradient, method = "L-BFGS-B", lower=0, upper=5) # Model prediction for fit parameters prediction <- odeC(y = c(myfit$par[1:3], yini_s, yini_chi, yini_grad), times = times, func = func, parms = myfit$par[4:5], forcings = forc, method = "lsodes") # Plot solution par(mfcol=c(1,2)) t <- prediction[,1] M1 <- prediction[,2:4] M2 <- prediction[,names(grad)] tD <- oxygenData[,1] M1D <- oxygenData[,2:4] matplot(t, M1, type="l", lty=1, col=1:3, xlab="time", ylab="value", main="states") matplot(tD, M1D, type="b", lty=2, col=1:3, pch=4, add=TRUE) legend("topright", legend = names(f), lty=1, col=1:3) matplot(t, M2, type="l", lty=1, col=1:5, xlab="time", ylab="value", main="gradient") legend("topleft", legend = names(grad), lty=1, col=1:5) ## End(Not run)
Generate interpolation spline for the forcings and write into list of matrices
setForcings(func, forcings)
setForcings(func, forcings)
func |
result from funC() |
forcings |
data.frame with columns name (factor), time (numeric) and value (numeric) |
Splines are generated for each name in forcings and both, function value and first derivative are evaluated at the time points of the data frame.
list of matrices with time points and values assigned to the forcings interface of deSolve
## Not run: f <- c(x = "-k*x + a - b") func <- funC(f, forcings = c("a", "b")) forcData <- rbind( data.frame(name = "a", time = c(0, 1, 10), value = c(0, 5, 2)), data.frame(name = "b", time = c(0, 5, 10), value = c(1, 3, 6))) forc <- setForcings(func, forcData) ## End(Not run)
## Not run: f <- c(x = "-k*x + a - b") func <- funC(f, forcings = c("a", "b")) forcData <- rbind( data.frame(name = "a", time = c(0, 1, 10), value = c(0, 5, 2)), data.frame(name = "b", time = c(0, 5, 10), value = c(1, 3, 6))) forc <- setForcings(func, forcData) ## End(Not run)
Compute matrix sumSymbolically
sumSymb(M, N)
sumSymb(M, N)
M |
matrix of type character |
N |
matrix of type character |
Matrix of type character, the matrix sum of M and N