Skip to content

Commit fa6bcc0

Browse files
author
Daniel Precioso, PhD
committed
Add interactive simulator links and expand unstable modes discussion in 2D Gierer-Meinhardt and Gray-Scott models
1 parent c41821b commit fa6bcc0

3 files changed

Lines changed: 150 additions & 153 deletions

File tree

modules/pde-1d/turing-instability.qmd

Lines changed: 8 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Patterns do not appear by accident. Turing instability explains when a uniform s
1010

1111
The previous page gave the intuition: local activation can reinforce a bump, inhibition can suppress nearby growth, and diffusion can either smooth or amplify spatial differences depending on the parameter regime. This page turns that picture into a linear stability test and asks a precise question, which parameter values and which spatial modes make the flat steady state lose stability?
1212

13+
::: {.callout-note}
14+
## Interactive Simulator
15+
16+
You can follow the theory on this page while running the [interactive 1D Gierer-Meinhardt simulator](https://bam-applied-math-lab.streamlit.app/Gierer_Meinhardt_1D). The simulator lets you pick a boundary condition, adjust domain length and reaction parameters, build a custom perturbation, and check which modes the simulation selects against the prediction from linear theory.
17+
:::
18+
1319
```{python}
1420
#| label: fig-pde-turing-space
1521
#| fig-cap: 'Turing space for the Gierer-Meinhardt model in the $(a, d)$ plane.'
@@ -236,7 +242,7 @@ plt.show()
236242
237243
Mark the point $(a, d) = (0.40, 20)$ on your plot. Is it inside the instability region?
238244
239-
This is the same Turing-space diagram that appears in the left panel of the interactive Gierer-Meinhardt code, so the algebra and the simulation refer to the same parameter plane.
245+
This is the same Turing-space diagram that appears in the [interactive 1D Gierer-Meinhardt simulator](https://bam-applied-math-lab.streamlit.app/Gierer_Meinhardt_1D), so the algebra and the simulation refer to the same parameter plane.
240246
241247
## Compute $F_n$ and the Unstable Mode List in 1D
242248
@@ -294,154 +300,6 @@ def find_unstable_modes_1d(a=0.40, b=1.00, d=30.0, length=40.0, num_modes=10):
294300
295301
The first entry in this list is the dominant linear mode, which predicts the earliest visible wavelength in the simulation.
296302
297-
## Extend the Scan to 2D
298-
299-
In two dimensions, the modes are pairs $(n_x, n_y)$. On a rectangle with side lengths $L_x$ and $L_y$ and Neumann boundaries,
300-
301-
$$
302-
\lambda_{n_x,n_y} = -\left(\frac{n_x\pi}{L_x}\right)^2 - \left(\frac{n_y\pi}{L_y}\right)^2.
303-
$$ {#eq-pde-turing-2d-modes}
304-
305-
The only real change is that you now scan a grid of mode pairs and build
306-
307-
$$
308-
F_{n_x,n_y} = J + \lambda_{n_x,n_y}\,\mathrm{diag}(1, d).
309-
$$
310-
311-
```python
312-
import numpy as np
313-
314-
def find_unstable_modes_2d(a=0.40, b=1.00, d=30.0, length_x=20.0, length_y=50.0, num_modes=8):
315-
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
316-
jac = np.array([[fu, fv], [gu, gv]])
317-
diffusion = np.diag([1.0, d])
318-
unstable_modes = []
319-
320-
for nx in range(num_modes):
321-
for ny in range(num_modes):
322-
if nx == 0 and ny == 0:
323-
continue
324-
325-
# TODO: compute lambda_nm and F_nm
326-
# TODO: store mode pairs with positive growth rate
327-
328-
return unstable_modes
329-
```
330-
331-
::: {.callout-tip collapse="true"}
332-
## Hint: 2D mode scan (click to expand)
333-
334-
```python
335-
def find_unstable_modes_2d(a=0.40, b=1.00, d=30.0, length_x=20.0, length_y=50.0, num_modes=8):
336-
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
337-
jac = np.array([[fu, fv], [gu, gv]])
338-
diffusion = np.diag([1.0, d])
339-
unstable_modes = []
340-
341-
for nx in range(num_modes):
342-
for ny in range(num_modes):
343-
if nx == 0 and ny == 0:
344-
continue
345-
346-
lambda_nm = -((nx * np.pi / length_x) ** 2 + (ny * np.pi / length_y) ** 2)
347-
f_nm = jac + lambda_nm * diffusion
348-
growth_rate = np.max(np.linalg.eigvals(f_nm).real)
349-
if growth_rate > 0:
350-
unstable_modes.append(((nx, ny), growth_rate))
351-
352-
unstable_modes.sort(key=lambda item: item[1], reverse=True)
353-
return unstable_modes
354-
```
355-
:::
356-
357-
For periodic boundaries, the same loop works after replacing each factor of $\pi / L$ by $2\pi / L$.
358-
359-
## Apply the Same Workflow to Gray-Scott
360-
361-
The theory on this page does not depend on the specific reaction law. Gray-Scott uses the same equilibrium to Jacobian to mode-matrix pipeline [@gray1984autocatalytic; @pearson1993complex].
362-
363-
Write the reaction terms as
364-
365-
$$
366-
R_u(u, v) = -u v^2 + f(1-u), \qquad R_v(u, v) = u v^2 - (f+k)v.
367-
$$
368-
369-
One homogeneous equilibrium is always
370-
371-
$$
372-
(u_*, v_*) = (1, 0).
373-
$$
374-
375-
If $v_* \neq 0$, then $u_* v_* = f + k$ and the nontrivial equilibria satisfy
376-
377-
$$
378-
u_* = \frac{1}{2}\left(1 \pm \sqrt{1 - \frac{4(f+k)^2}{f}}\right), \qquad v_* = \frac{f+k}{u_*},
379-
$$ {#eq-pde-turing-gs-steady}
380-
381-
whenever $1 - 4(f+k)^2/f \ge 0$.
382-
383-
The Gray-Scott Jacobian at a homogeneous equilibrium is
384-
385-
$$
386-
J_{\mathrm{GS}} =
387-
\begin{pmatrix}
388-
-v_*^2 - f & -2u_*v_* \\
389-
v_*^2 & 2u_*v_* - (f+k)
390-
\end{pmatrix}.
391-
$$ {#eq-pde-turing-gs-jacobian}
392-
393-
At the trivial state $(1, 0)$, this Jacobian is diagonal and every linear mode decays. So the classical Turing test is only interesting at a nontrivial homogeneous equilibrium when one exists.
394-
395-
For periodic boundaries on a rectangle,
396-
397-
$$
398-
\lambda_{m,n} = -\left(\frac{2\pi m}{L_x}\right)^2 - \left(\frac{2\pi n}{L_y}\right)^2,
399-
$$
400-
401-
and the mode matrix is
402-
403-
$$
404-
F^{\mathrm{GS}}_{m,n} = J_{\mathrm{GS}} + \lambda_{m,n}\,\mathrm{diag}(d_1, d_2).
405-
$$
406-
407-
```python
408-
import numpy as np
409-
410-
def gray_scott_equilibria(f=0.040, k=0.060):
411-
equilibria = [(1.0, 0.0)]
412-
discriminant = 1 - 4 * (f + k) ** 2 / f
413-
414-
if discriminant >= 0:
415-
root = np.sqrt(discriminant)
416-
for sign in (+1, -1):
417-
u_star = 0.5 * (1 + sign * root)
418-
if u_star > 0:
419-
equilibria.append((u_star, (f + k) / u_star))
420-
421-
return equilibria
422-
423-
424-
def gray_scott_jacobian(u_star, v_star, f=0.040, k=0.060):
425-
return np.array(
426-
[
427-
[-v_star**2 - f, -2 * u_star * v_star],
428-
[v_star**2, 2 * u_star * v_star - (f + k)],
429-
]
430-
)
431-
```
432-
433-
That is the whole bridge from Gierer-Meinhardt to Gray-Scott. Keep the eigenvalue scan, replace the Jacobian, replace the boundary spectrum, then sort the unstable modes and read off the dominant one.
434-
435-
## Compare Two Cases
436-
437-
Use your functions to compare $d=20$ and $d=30$ with $a=0.40$ and $b=1.00$.
438-
439-
1. Does each case satisfy the Turing conditions?
440-
2. How many unstable modes appear in 1D?
441-
3. What is the leading mode in 2D?
442-
443-
These answers will help you interpret the 1D assignment now and the 2D simulations in the next session.
444-
445303
## What's Next?
446304
447305
Now that you know how to predict pattern-forming parameter regimes, close the 1D loop by completing the assignment. After that, carry the same ideas to a 2D grid in the next session.
@@ -450,5 +308,5 @@ Now that you know how to predict pattern-forming parameter regimes, close the 1D
450308
[PDEs in 2D](../pde-2d/index.qmd){.btn .btn-primary}
451309
452310
::: {.callout-tip}
453-
Reference code is available in [amlab/pdes/gierer_meinhardt_1d.py](https://github.com/daniprec/BAM-Applied-Math-Lab/blob/main/amlab/pdes/gierer_meinhardt_1d.py) and [amlab/pdes/gierer_meinhardt_2d.py](https://github.com/daniprec/BAM-Applied-Math-Lab/blob/main/amlab/pdes/gierer_meinhardt_2d.py).
311+
Reference code is available in [amlab/pdes/gierer_meinhardt_1d.py](https://github.com/daniprec/BAM-Applied-Math-Lab/blob/main/amlab/pdes/gierer_meinhardt_1d.py).
454312
:::

modules/pde-2d/gierer-meinhardt-2d.qmd

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,79 @@ def update_frame(_):
326326
327327
The 5-point stencil is the standard choice, but you can improve the rotational symmetry of the Laplacian with a 9-point stencil. That is a good extension if you want to compare numerical accuracy.
328328
329-
## Explore
329+
## Unstable Modes on a Rectangle
330330
331-
Use the Turing page to compare the cases $d=20$ and $d=30$. Does the pattern that forms on your grid match the change suggested by the unstable mode analysis?
331+
The Turing instability page showed how to list unstable modes in 1D by scanning the Neumann eigenvalues $\lambda_n = -(n\pi/L)^2$. In two dimensions, the modes are pairs $(n_x, n_y)$. On a rectangle with side lengths $L_x$ and $L_y$ and Neumann boundaries,
332+
333+
$$
334+
\lambda_{n_x,n_y} = -\left(\frac{n_x\pi}{L_x}\right)^2 - \left(\frac{n_y\pi}{L_y}\right)^2.
335+
$$ {#eq-pde-turing-2d-modes}
336+
337+
The mode matrix is the same as before:
338+
339+
$$
340+
F_{n_x,n_y} = J + \lambda_{n_x,n_y}\,\mathrm{diag}(1, d).
341+
$$
342+
343+
Extend your 1D mode scan to two dimensions.
344+
345+
```python
346+
import numpy as np
347+
348+
def find_unstable_modes_2d(a=0.40, b=1.00, d=30.0, length_x=20.0, length_y=50.0, num_modes=8):
349+
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
350+
jac = np.array([[fu, fv], [gu, gv]])
351+
diffusion = np.diag([1.0, d])
352+
unstable_modes = []
353+
354+
for nx in range(num_modes):
355+
for ny in range(num_modes):
356+
if nx == 0 and ny == 0:
357+
continue
358+
359+
# TODO: compute lambda_nm and F_nm
360+
# TODO: store mode pairs with positive growth rate
361+
362+
return unstable_modes
363+
```
364+
365+
::: {.callout-tip collapse="true"}
366+
## Hint: 2D mode scan (click to expand)
367+
368+
```python
369+
def find_unstable_modes_2d(a=0.40, b=1.00, d=30.0, length_x=20.0, length_y=50.0, num_modes=8):
370+
fu, fv, gu, gv = gierer_meinhardt_jacobian(a, b)
371+
jac = np.array([[fu, fv], [gu, gv]])
372+
diffusion = np.diag([1.0, d])
373+
unstable_modes = []
374+
375+
for nx in range(num_modes):
376+
for ny in range(num_modes):
377+
if nx == 0 and ny == 0:
378+
continue
379+
380+
lambda_nm = -((nx * np.pi / length_x) ** 2 + (ny * np.pi / length_y) ** 2)
381+
f_nm = jac + lambda_nm * diffusion
382+
growth_rate = np.max(np.linalg.eigvals(f_nm).real)
383+
if growth_rate > 0:
384+
unstable_modes.append(((nx, ny), growth_rate))
385+
386+
unstable_modes.sort(key=lambda item: item[1], reverse=True)
387+
return unstable_modes
388+
```
389+
:::
390+
391+
For periodic boundaries, replace each factor of $\pi / L$ by $2\pi / L$.
392+
393+
## Compare $d = 20$ and $d = 30$
394+
395+
Use the functions above to compare $d = 20$ and $d = 30$ with $a = 0.40$ and $b = 1.00$.
396+
397+
1. Does each case satisfy the Turing conditions?
398+
2. How many unstable mode pairs $(n_x, n_y)$ appear on your rectangle?
399+
3. What is the dominant mode pair?
400+
401+
Then run the simulation for each case and check whether the observed stripe or spot pattern matches the dominant mode prediction.
332402
333403
## What's Next?
334404

modules/pde-2d/gray-scott.qmd

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,76 @@ The same steady-state to Jacobian to mode-matrix pipeline from the Turing page s
210210
- For Neumann boundaries, the admissible wavenumbers use factors of $\pi / L$.
211211
- For periodic boundaries, the admissible wavenumbers use factors of $2\pi / L$.
212212
213-
For the usual base state $(u, v) = (1, 0)$, the linearized system is damped, so the classical Turing calculation only becomes interesting at a nontrivial homogeneous equilibrium when one exists. In that regime, the mode matrices are built exactly as before, but with the periodic Laplacian eigenvalues. That is the clean conceptual bridge from Gierer-Meinhardt to Gray-Scott.
213+
For the usual base state $(u, v) = (1, 0)$, the linearized system is damped, so the classical Turing calculation only becomes interesting at a nontrivial homogeneous equilibrium when one exists.
214+
215+
The nontrivial homogeneous equilibria satisfy $u_* v_* = f + k$, and one can show that
216+
217+
$$
218+
u_* = \frac{1}{2}\left(1 \pm \sqrt{1 - \frac{4(f+k)^2}{f}}\right), \qquad v_* = \frac{f+k}{u_*},
219+
$$ {#eq-pde-gs-steady}
220+
221+
whenever $1 - 4(f+k)^2/f \ge 0$. The Gray-Scott Jacobian at any homogeneous equilibrium is
222+
223+
$$
224+
J_{\mathrm{GS}} =
225+
\begin{pmatrix}
226+
-v_*^2 - f & -2u_*v_* \\
227+
v_*^2 & 2u_*v_* - (f+k)
228+
\end{pmatrix}.
229+
$$ {#eq-pde-gs-jacobian}
230+
231+
Implement both helpers and run the mode-matrix eigenvalue scan with the periodic spectrum to find unstable modes for a given $(f, k, d_1, d_2)$.
232+
233+
```python
234+
import numpy as np
235+
236+
def gray_scott_equilibria(f=0.040, k=0.060):
237+
equilibria = [(1.0, 0.0)]
238+
discriminant = 1 - 4 * (f + k) ** 2 / f
239+
240+
if discriminant >= 0:
241+
root = np.sqrt(discriminant)
242+
for sign in (+1, -1):
243+
u_star = 0.5 * (1 + sign * root)
244+
# TODO: append the nontrivial equilibrium if u_star > 0
245+
246+
return equilibria
247+
248+
249+
def gray_scott_jacobian(u_star, v_star, f=0.040, k=0.060):
250+
# TODO: return the 2x2 Jacobian array
251+
pass
252+
```
253+
254+
::: {.callout-tip collapse="true"}
255+
## Hint: Gray-Scott equilibria and Jacobian (click to expand)
256+
257+
```python
258+
def gray_scott_equilibria(f=0.040, k=0.060):
259+
equilibria = [(1.0, 0.0)]
260+
discriminant = 1 - 4 * (f + k) ** 2 / f
261+
262+
if discriminant >= 0:
263+
root = np.sqrt(discriminant)
264+
for sign in (+1, -1):
265+
u_star = 0.5 * (1 + sign * root)
266+
if u_star > 0:
267+
equilibria.append((u_star, (f + k) / u_star))
268+
269+
return equilibria
270+
271+
272+
def gray_scott_jacobian(u_star, v_star, f=0.040, k=0.060):
273+
return np.array(
274+
[
275+
[-v_star**2 - f, -2 * u_star * v_star],
276+
[v_star**2, 2 * u_star * v_star - (f + k)],
277+
]
278+
)
279+
```
280+
:::
281+
282+
Once both helpers work, reuse `find_unstable_modes_2d` from the Gierer-Meinhardt 2D page, swap in `gray_scott_jacobian`, and replace $\pi / L$ with $2\pi / L$ to get the periodic mode spectrum. That is the direct conceptual bridge from Gierer-Meinhardt to Gray-Scott [@gray1984autocatalytic; @pearson1993complex].
214283
215284
## Optional: 9-Point Stencil
216285

0 commit comments

Comments
 (0)