Consider a reaction-diffusion system with two components \(u(x, t)\) and \(v(x, t)\): \[ \begin{align} \frac{\partial u(x,t)}{\partial t} & = \gamma \cdot f(u,v) + \frac{\partial^2 u(x,t)}{\partial x^2}, \\ \frac{\partial v(x,t)}{\partial t} & = \gamma \cdot g(u,v) + d \cdot \frac{\partial^2 v(x,t)}{\partial x^2} \end{align} \] At the boundary, \(u\) and \(v\) satisfy the zero-flux condition. We will simulate the system numerically in discrete space. Based on \(N\) equidistant supporting points, the distance between neighboring points is \(\Delta x = \frac{1}{N-1}\). The resulting approximation of the diffusion term reads
\[ \begin{align} \frac{\partial^2 u(x,t)}{\partial x^2} \quad \longrightarrow \quad \frac{u(x - \Delta x) - 2 u(x) + u(x + \Delta x)}{(\Delta x)^2}, \end{align} \] and analogously for \(v\). Thereby, the partial differential equation turns into a system of ordinary differential equations.
## Plotting functions
plotOut <- function(out, mode = c("time", "space")) {
t <- out[,1]
x <- 1:N
u <- out[,1:N+1]
v <- out[,(N+1):(2*N)+1]
mydata <- rbind(
data.frame(time = t, x = rep(x, each = length(t)), name = "u", value = as.vector(u)),
data.frame(time = t, x = rep(x, each = length(t)), name = "v", value = as.vector(v)))
mode <- match.arg(mode)
if (mode == "time") {
P <- ggplot(mydata, aes(x = time, y = value, group = x, color = x)) +
facet_wrap(~name, scales="free") +
geom_line()
}
if (mode == "space") {
P <- ggplot(mydata, aes(x = x, y = value, group = time, color = time)) +
facet_wrap(~name, scales="free") +
geom_line()
}
return(P)
}
Implement the linear Turing system with the reaction functions
\[ \begin{align} f(u,v) & = a \cdot u + b \cdot v, \\ g(u,v) & = c \cdot u + e \cdot v, \end{align} \] with parameters \(a = -2\), \(b = 2.5\), \(c = -1.25\), \(\gamma = 1000\), \(d = 0.5\), \(e = 1.5\), and \(N = 250\). Let the initial values \(u_{1...N} \propto N(0,1)\) and \(v_{1...N} \propto N(0,1)\) be normally distributed random numbers.
# load packages needed for the exercises
library(deSolve)
library(ggplot2)
theme_set(theme_bw())
## Turing system
Turing <- function(Time, State, Pars, N) {
with(as.list(Pars), {
# Recover u and v from State vector
u <- State[1:N]
v <- State[(N+1):(2*N)]
dia <- c(-1, rep(-2, N-2), -1)
# Boundary conditions for u and v
uL <- c(u[-1], 0)
uR <- c(0, u[-length(u)])
vL <- c(v[-1], 0)
vR <- c(0, v[-length(v)])
# Reaction part of the system
f1 <- a*u + b*v
g1 <- c*u + e*v
# Reaction-diffusion system
deltax <- 1/(N-1)
du <- g * f1 + (uL + dia*u + uR)/deltax^2
dv <- g * g1 + d * (vL + dia*v + vR)/deltax^2
return(list(c(du, dv)))
})
}
# Set times and parameters and compute result
N <- 250
pars <- c(
a = -2,
b = 2.5,
c = -1.25,
d = 0.5,
e = 1.5,
g = 1000
)
yini <- rnorm(2*N, 0, 1)
times <- c(0, exp(seq(log(1e-3), log(1e1), length.out=200)))
out <- ode(method="lsodes", func = Turing, y = yini, parms = pars, times = times, N = N)
# Plot the result
plotOut(out, "time") + scale_x_log10()
## Warning: Transformation introduced infinite values in continuous x-axis
plotOut(out, "space")
Implement the (dimensionless) activator-inhibitor model with the reaction functions
\[ \begin{align} f(u,v) & = a - b\cdot u + \frac{u^2}{v}, \\ g(u,v) & = u^2 - v, \end{align} \]
and parameters \(a = 0.1\), \(b = 1\), \(\gamma = 100\), \(d = 10\), and \(N = 250\). Use the homogeneous equilibrium states, \[ \begin{align} u_{1...N} = \frac{a + 1}{b} \quad \text{and} \quad v_{1...N} = \left( \frac{a + 1}{b} \right)^2, \end{align} \] and add noise to obtain initial values.
# Gierer-Meinhardt model
Gierer <- function(Time, State, Pars, N) {
with(as.list(Pars), {
# Recover u and v from State vector
u <- State[1:N]
v <- State[(N+1):(2*N)]
dia <- c(-1, rep(-2, N-2), -1)
# Boundary conditions for u and v
uL <- c(u[-1], 0)
uR <- c(0, u[-length(u)])
vL <- c(v[-1], 0)
vR <- c(0, v[-length(v)])
# Reaction part of the system
f2 <- a-b*u+(u^2)/v
g2 <- u^2 - v
# Reaction-diffusion system
deltax <- 1/(N-1)
du <- g * f2 + (uL + dia*u + uR)/deltax^2
dv <- g * g2 + d * (vL + dia*v + vR)/deltax^2
return(list(c(du, dv)))
})
}
# Set times and parameters and compute result
N <- 250
pars <- c(
a = .1,
b = 1,
g = 100,
d = 10
)
times <- c(0, exp(seq(log(1e-3), log(1e0), length.out=200)))
steady <- with(as.list(pars), c(u = (a+1)/b, v = (a+1)^2/b^2))
solveGierer <- function(n, gamma = 100, noise = 0) {
pars["g"] <- gamma
a <- pars["a"]
b <- pars["b"]
yini <- rep(c((a+1)/b, (a+1)^2/b^2), each = N)
if (n >= 0)
yini <- yini + c(0.1*cos(2*n*pi*c(1:N)/N), rep(0, each = N))
if (noise > 0)
yini <- yini + rnorm(length(yini), 0, noise)
out <- ode(method = "lsodes", func = Gierer, y = yini, parms = pars, times = times, N = N)
}
set.seed(0)
out <- solveGierer(n = -1, noise = .1)
plotOut(out, "space")
out <- solveGierer(n = -1, noise = .1)
plotOut(out, "space")
out <- solveGierer(n = -1, noise = .1)
plotOut(out, "space")
out <- solveGierer(n = -1, noise = .1)
plotOut(out, "space")
set.seed(0)
out <- solveGierer(n = -1, noise = .1, gamma = 100)
plotOut(out, "space")
set.seed(0)
out <- solveGierer(n = -1, noise = .1, gamma = 400)
plotOut(out, "space")
set.seed(0)
out <- solveGierer(n = -1, noise = .1, gamma = 900)
plotOut(out, "space")
library(numDeriv)
# Get the dispersion relation
dispersion <- function(n, steady, pars) {
# Reaction function
reaction <- function(state, pars) {
with(as.list(c(state, pars)), {
f <- a - b*u + u^2/v
g <- u^2 -v
return(c(f, g))
})
}
# Compute eigenvalues of J - k² D
with(as.list(pars), {
unlist(lapply(n, function(myn) {
M <- g*jacobian(reaction, x = steady, pars = pars) - (2*pi*myn)^2 * diag(c(1, d))
Re(eigen(M, only.values = TRUE)$values[2])
}))
})
}
n <- seq(0, 2, length.out = 200)
lambda <- dispersion(n, steady, pars)
roots <- n[diff(sign(lambda)) != 0]
qplot(x = n, y = lambda) + geom_hline(yintercept = 0) + geom_vline(xintercept = roots)
out <- solveGierer(n = 0, noise = 0, gamma = 100)
plotOut(out, "time") + scale_x_sqrt()
out <- solveGierer(n = 1, noise = 0, gamma = 100)
plotOut(out, "time") + scale_x_sqrt()
plotOut(out, "space")
out <- solveGierer(n = 2, noise = 0, gamma = 100)
plotOut(out, "time") + scale_x_sqrt()
plotOut(out, "space")
out <- solveGierer(n = 3, noise = 0, gamma = 100)
plotOut(out, "time") + scale_x_sqrt()
out <- solveGierer(n = 4, noise = 0, gamma = 100)
plotOut(out, "time") + scale_x_sqrt()
What distinguished our cathedral from all other German gothic cathedrals?