Thomas Petzoldt
6th February 2017
\[ \frac{dN}{dt} = b\cdot N - d\cdot N\\ \frac{dN}{dt} = (b-d) N = rN \]
\[ N_t = N_0 e^{rt} \]
This is the exponential growth model.
r <- 0.5
N0 <- 10
dt <- 0.1
time <- seq(0, 10, dt)
# analytical solution
N <- N0 * exp(r * time)
plot(time, N, type="l")
N <- numeric(length(time))
N[1] <- N0
for (i in 2:length(time)) {
N[i] <- N[i-1] + r * N[i-1] * dt
}
plot(time, N, type = "l")
library(deSolve)
model <- function (time, y, parms) {
with(as.list(c(y, parms)), {
dN <- r * N
list(c(dN))
})
}
y0 <- c(N = 0.1)
parms <- c(r = 0.1, K = 10)
times <- seq(0, 100, 1)
out <- ode(y0, times, model, parms) # <---
plot(out)
library(deSolve)
model <- function (time, y, parms) {
with(as.list(c(y, parms)), {
dN <- r * N * (1 - N / K)
list(c(dN))
})
}
y0 <- c(N = 0.1)
parms <- c(r = 0.1, K = 10)
times <- seq(0, 100, 1)
out <- ode(y0, times, model, parms) # <---
plot(out)
rk4: very popular, easy to implement, but: either inaccurate or slow
\[ f(P) = \frac{P}{k_P + P}\\ \frac{dAlg}{dt} = r \cdot f(P) \cdot Alg\\ \frac{dP}{dt} = - r \cdot 1/Y \cdot f(P) \cdot Alg \]
model <- function (time, y, parms) {
with(as.list(c(y, parms)), {
f <- P/(kP + P)
dAlg <- r * f * Alg
dP <- - r * 1/Y * f * Alg
list(c(dAlg, dP))
})
}
y <- c(Alg = 0.1, P = 0.2) # in mg/L
parms <- c(r = 0.1, kP = 5e-3, Y = 41) # Y = C:P mass ratio
times <- seq(0, 100, 1)
out <- ode(y, times, model, parms)
plot(out)
library(marelac)
redfield(1, species="P")
## C H O N P
## 1 106 263 110 16 1
redfield(1, species="P", method="mass")
## C H O N P
## 1 41.10363 8.558477 56.82016 7.235388 1
molweight("C2H5OH")
## C2H5OH
## 46.06844
chemostat <- function(time, init, parms) {
with(as.list(c(init, parms)), {
mu <- mumax * P/(kp + P) # Monod equation
dAlg <- mu * Alg - D * Alg
dP <- D *(P0 - P) - 1/Y * mu * Alg
list(c(dAlg, dP), mu=mu)
})
}
parms <- c(
mumax = 0.5, # 1/d
kp = 0.01, # half saturation constant (mg/L)
Y = 41, # yield coefficient
D = 0.1, # 1/d
P0 = 0.05 # P in inflow (mg/L)
)
times <- seq(0, 40, 0.1) # (d)
init <- c(Alg=2, P=0.05) # Phytoplankton C and Phosphorus P (mg/L)
out <- ode(init, times, chemostat, parms)
plot(out, type="l")
library(deSolve)
batch <- function(time, y, parms){
with(as.list(c(y, parms)), {
f <- r * S / (ks + S)
dS <- - 1/Y * f * N
dN <- f * N
return(list(c(dS, dN)))
})
}
y <- c(S = 10, N = 1e4)
parms <- c(r=1, ks=5, Y=1e6, S0=10)
times <- seq(0, 20, 0.1)
out <- ode(y, times, batch, parms)
plot(out)
parms <- c(r=1, ks=5, Y=1e6, S0=10, D=0.5)
times <- seq(0, 50, 0.01)
etime <- seq(5, 50, 5) # time points to triger events
eventfun <- function(t, y, parms) { # event function
with(as.list(c(y, parms)), {
return(c(D * S0 + (1-D) * S, (1-D) * N)) # D = dilution rate
})
}
out <- ode(y, times, batch, parms,
events = list(func = eventfun, time = etime))
plot(out)
library(deSolve)
batch <- function(time, y, parms){
with(as.list(c(y, parms)), {
f <- r * S / (ks + S)
dS <- - 1/Y * f * N
dN <- f * N
return(list(c(dS, dN)))
})
}
y <- c(S = 10, N = 1e4)
parms <- c(r=1, ks=5, Y=1e6, S0=10)
times <- seq(0, 20, 0.1)
parms <- c(r=1, ks=5, Y=1e6, S0=10, D=0.5)
times <- seq(0, 50, 0.01)
etime <- seq(5, 50, 5) # time points to triger events
eventfun <- function(t, y, parms) { # event function
with(as.list(c(y, parms)), {
return(c(D * S0 + (1-D) * S, (1-D) * N)) # D = dilution rate
})
}
crit <- 0.9e7 # critical cell number that triggers dilution event
rootfun <- function (t, y, pars) return(crit - y[2])
out <- ode(y, times, batch, parms,
events = list(func = eventfun, root = TRUE), rootfun = rootfun)
plot(out)
And to you.
http://desolve.r-forge.r-project.org/
http://desolve.r-forge.r-project.org/user2014/tutorial.pdf
http://desolve.r-forge.r-project.org/user2014/examples/vanderpol/vanderpol.svg
http://desolve.r-forge.r-project.org/user2014/examples/FME/fit_twocomp.svg
http://desolve.r-forge.r-project.org/user2014/examples/compiled_lorenz/compiledcode.svg