#' Ordinary Differential Equations
#'
#' Solves a numerical or symbolic system of ordinary differential equations.
#'
#' @param f vector of \code{characters}, or a \code{function} returning a numeric vector, giving the values of the derivatives in the ODE system at time \code{timevar}. See examples.
#' @param var vector giving the initial conditions. See examples.
#' @param times discretization sequence, the first value represents the initial time.
#' @param timevar the time variable used by \code{f}, if any.
#' @param params \code{list} of additional parameters passed to \code{f}.
#' @param method the solver to use. One of \code{"rk4"} (Runge-Kutta) or \code{"euler"} (Euler).
#' @param drop if \code{TRUE}, return only the final solution instead of the whole trajectory.
#'
#' @return Vector of final solutions if \code{drop=TRUE}, otherwise a \code{matrix} with as many
#' rows as elements in \code{times} and as many columns as elements in \code{var}.
#'
#' @examples
#' ## ==================================================
#' ## Example: symbolic system
#' ## System: dx = x dt
#' ## Initial: x0 = 1
#' ## ==================================================
#' f <- "x"
#' var <- c(x=1)
#' times <- seq(0, 2*pi, by=0.001)
#' x <- ode(f, var, times)
#' plot(times, x, type = "l")
#'
#' ## ==================================================
#' ## Example: time dependent system
#' ## System: dx = cos(t) dt
#' ## Initial: x0 = 0
#' ## ==================================================
#' f <- "cos(t)"
#' var <- c(x=0)
#' times <- seq(0, 2*pi, by=0.001)
#' x <- ode(f, var, times, timevar = "t")
#' plot(times, x, type = "l")
#'
#' ## ==================================================
#' ## Example: multivariate time dependent system
#' ## System: dx = x dt
#' ## dy = x*(1+cos(10*t)) dt
#' ## Initial: x0 = 1
#' ## y0 = 1
#' ## ==================================================
#' f <- c("x", "x*(1+cos(10*t))")
#' var <- c(x=1, y=1)
#' times <- seq(0, 2*pi, by=0.001)
#' x <- ode(f, var, times, timevar = "t")
#' matplot(times, x, type = "l", lty = 1, col = 1:2)
#'
#' ## ==================================================
#' ## Example: numerical system
#' ## System: dx = x dt
#' ## dy = y dt
#' ## Initial: x0 = 1
#' ## y0 = 2
#' ## ==================================================
#' f <- function(x, y) c(x, y)
#' var <- c(x=1, y=2)
#' times <- seq(0, 2*pi, by=0.001)
#' x <- ode(f, var, times)
#' matplot(times, x, type = "l", lty = 1, col = 1:2)
#'
#' ## ==================================================
#' ## Example: vectorized interface
#' ## System: dx = x dt
#' ## dy = y dt
#' ## dz = y*(1+cos(10*t)) dt
#' ## Initial: x0 = 1
#' ## y0 = 2
#' ## z0 = 2
#' ## ==================================================
#' f <- function(x, t) c(x[1], x[2], x[2]*(1+cos(10*t)))
#' var <- c(1,2,2)
#' times <- seq(0, 2*pi, by=0.001)
#' x <- ode(f, var, times, timevar = "t")
#' matplot(times, x, type = "l", lty = 1, col = 1:3)
#'
#' @family integrals
#'
#' @references
#' Guidotti E (2022). "calculus: High-Dimensional Numerical and Symbolic Calculus in R." Journal of Statistical Software, 104(5), 1-37. \doi{10.18637/jss.v104.i05}
#'
#' @export
#'
ode <- function(f, var, times, timevar = NULL, params = list(), method = "rk4", drop = FALSE){
is.named <- !is.null(names(var))
is.fun <- is.function(f)
is.t <- !is.null(timevar)
h <- diff(times)
n <- length(h)
if(!is.fun){
f <- e2c(f)
f <- sprintf("c(%s)", paste(f, collapse = ","))
f <- c2e(f)
}
if(!drop){
m <- matrix(nrow = n+1, ncol = length(var), dimnames = list(times, names(var)))
m[1,] <- var
}
if(is.t)
names(times) <- rep(timevar, n+1)
if(method=="euler"){
if(is.fun){
if(!is.named){
if(!is.t){
for(i in 1:n){
var <- var + h[i] * do.call(f, c(list(var), params))
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
var <- var + h[i] * do.call(f, c(list(var), as.list(times[i]), params))
if(!drop) m[i+1,] <- var
}
}
}
else {
if(!is.t){
for(i in 1:n){
var <- var + h[i] * do.call(f, c(as.list(var), params))
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
var <- var + h[i] * do.call(f, c(as.list(c(var, times[i])), params))
if(!drop) m[i+1,] <- var
}
}
}
} else {
env <- list2env(params, hash = TRUE)
if(!is.t){
for(i in 1:n){
list2env(as.list(var), envir = env)
var <- var + h[i] * eval(f, envir = env)
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
list2env(as.list(c(var, times[i])), envir = env)
var <- var + h[i] * eval(f, envir = env)
if(!drop) m[i+1,] <- var
}
}
}
}
else if(method=="rk4") {
if(is.fun){
if(!is.named){
if(!is.t){
for(i in 1:n){
k1 <- do.call(f, c(list(var), params))
k2 <- do.call(f, c(list(var+h[i]*k1/2), params))
k3 <- do.call(f, c(list(var+h[i]*k2/2), params))
k4 <- do.call(f, c(list(var+h[i]*k3), params))
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
k1 <- do.call(f, c(list(var), as.list(times[i]), params))
k2 <- do.call(f, c(list(var+h[i]*k1/2), as.list(times[i]+h[i]/2), params))
k3 <- do.call(f, c(list(var+h[i]*k2/2), as.list(times[i]+h[i]/2), params))
k4 <- do.call(f, c(list(var+h[i]*k3), as.list(times[i]+h[i]), params))
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
}
else {
if(!is.t){
for(i in 1:n){
k1 <- do.call(f, c(as.list(var), params))
k2 <- do.call(f, c(as.list(var+h[i]*k1/2), params))
k3 <- do.call(f, c(as.list(var+h[i]*k2/2), params))
k4 <- do.call(f, c(as.list(var+h[i]*k3), params))
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
k1 <- do.call(f, c(as.list(c(times[i], var)), params))
k2 <- do.call(f, c(as.list(c(times[i]+h[i]/2, var+h[i]*k1/2)), params))
k3 <- do.call(f, c(as.list(c(times[i]+h[i]/2, var+h[i]*k2/2)), params))
k4 <- do.call(f, c(as.list(c(times[i]+h[i], var+h[i]*k3)), params))
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
}
}
else {
env1 <- list2env(params, hash = TRUE)
env2 <- list2env(params, hash = TRUE)
env3 <- list2env(params, hash = TRUE)
env4 <- list2env(params, hash = TRUE)
if(!is.t){
for(i in 1:n){
list2env(as.list(var), envir = env1)
k1 <- eval(f, envir = env1)
list2env(as.list(var+h[i]*k1/2), envir = env2)
k2 <- eval(f, envir = env2)
list2env(as.list(var+h[i]*k2/2), envir = env3)
k3 <- eval(f, envir = env3)
list2env(as.list(var+h[i]*k3), envir = env4)
k4 <- eval(f, envir = env4)
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
else{
for(i in 1:n){
list2env(as.list(c(times[i], var)), envir = env1)
k1 <- eval(f, envir = env1)
list2env(as.list(c(times[i]+h[i]/2, var+h[i]*k1/2)), envir = env2)
k2 <- eval(f, envir = env2)
list2env(as.list(c(times[i]+h[i]/2, var+h[i]*k2/2)), envir = env3)
k3 <- eval(f, envir = env3)
list2env(as.list(c(times[i]+h[i], var+h[i]*k3)), envir = env4)
k4 <- eval(f, envir = env4)
var <- var + (h[i]*(k1+2*k2+2*k3+k4))/6
if(!drop) m[i+1,] <- var
}
}
}
} else {
stop("method not supported.")
}
if(!drop)
return(m)
return(var)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.