Turing Numerics
Module 4: Space and Pattern Formation
This is the prediction step of the laboratory. You turn the theory of Turing Conditions and Dispersion Relation into three small functions: equilibrium, Jacobian, and Turing test (Turing 1952; murray2003mathematical?). The fourth, the mode scan, follows on Unstable Modes in 1D. You will reuse all four in 2D and in the assignment.
The workflow is always the same:
- Find the homogeneous equilibrium.
- Compute the Jacobian at that equilibrium.
- Compute the Laplacian eigenvalues from the domain and the boundary conditions.
- Build the mode matrix \(A_n = \gamma J - k_n^2 D\) for each mode.
- Compute the eigenvalues of each \(2 \times 2\) matrix and keep the modes whose largest real part is positive.
Compute the Equilibrium
The steady state from Linearization is one line of code each.
def gierer_meinhardt_equilibrium(a=0.40, b=1.00):
u_star = (a + 1) / b
v_star = u_star**2
return u_star, v_starWrite the Jacobian at the Fixed Point
Use the general Jacobian from Linearization and evaluate it at \((u_*, v_*)\).
def gierer_meinhardt_jacobian(a=0.40, b=1.00):
u_star, v_star = gierer_meinhardt_equilibrium(a, b)
# TODO: return fu, fv, gu, gvdef gierer_meinhardt_jacobian(a=0.40, b=1.00):
u_star, v_star = gierer_meinhardt_equilibrium(a, b)
fu = -b + 2 * u_star / v_star
fv = -(u_star**2) / (v_star**2)
gu = 2 * u_star
gv = -1.0
return fu, fv, gu, gvSimplified, this is \(f_u = b(1-a)/(1+a)\), \(f_v = -b^2/(1+a)^2\), \(g_u = 2(1+a)/b\), \(g_v = -1\). You can also write the entries directly in \(a\) and \(b\):
def gierer_meinhardt_jacobian(a=0.40, b=1.00):
fu = 2 * b / (a + 1) - b
fv = -((b / (a + 1)) ** 2)
gu = 2 * (a + 1) / b
gv = -1.0
return fu, fv, gu, gvImplement the Turing Test
Translate the four inequalities of (murray2003mathematical?) into Python. Use & instead of and so the function works on arrays.
import numpy as np
def is_turing_instability(a, b, d):
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
det = fu * gv - fv * gu
# TODO: write the four conditions
cond1 =
cond2 =
cond3 =
cond4 =
return cond1 & cond2 & cond3 & cond4def is_turing_instability(a, b, d):
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
det = fu * gv - fv * gu
cond1 = (fu + gv) < 0
cond2 = det > 0
cond3 = (d * fu + gv) > 0
cond4 = (d * fu + gv) ** 2 > 4 * d * det
return cond1 & cond2 & cond3 & cond4The course package writes the last two conditions as the single inequality \(d f_u + g_v > 2\sqrt{d \det J}\), which is equivalent.
If your implementation is vectorized, it works with scalars and with arrays. Validate it carefully: you reuse it in 2D and in the assignment.
Plot the Turing Space
Complete the code to recreate the diagram on Turing Space. Mark the points \((a, d) = (0.4, 20)\) and \((0.4, 30)\). Which one lies inside the instability region?
import matplotlib.pyplot as plt
import numpy as np
arr_a = np.linspace(0, 1, 500)
arr_d = np.linspace(0, 100, 500)
mesh_a, mesh_d = np.meshgrid(arr_a, arr_d)
mask_turing = # TODO: evaluate the Turing test on the mesh, with b = 1
fig, ax = plt.subplots(figsize=(7, 4))
ax.contourf(mesh_a, mesh_d, mask_turing)
# TODO: mark the two test points
ax.set_xlabel("a")
ax.set_ylabel("d")
ax.set_title("Turing Space")
plt.show()Pass the whole mesh to the vectorized test. The point \((0.4, 30)\) lies inside the region and \((0.4, 20)\) lies outside, because the boundary at \(a = 0.4\) sits at \(d_c \approx 26.24\).
mask_turing = is_turing_instability(mesh_a, 1.0, mesh_d)
fig, ax = plt.subplots(figsize=(7, 4))
ax.contourf(mesh_a, mesh_d, mask_turing)
ax.plot([0.4], [30], "o", color="tab:red", label="Case A: d = 30")
ax.plot([0.4], [20], "s", color="tab:gray", label="Case B: d = 20")
ax.set_xlabel("a")
ax.set_ylabel("d")
ax.set_title("Turing Space")
ax.legend()
plt.show()The same diagram appears in the interactive 1D Gierer-Meinhardt simulator, so the algebra and the simulation refer to the same parameter plane.