Animation in 2D
Module 4: Space and Pattern Formation
Once the 2D Euler loop on Gierer-Meinhardt Solver in 2D passes the time-step check, you can look at the field. Plot one static snapshot first. Animate only after the snapshot looks right, as in Animation in 1D.
Plot a Snapshot
imshow draws the matrix uv[1]. Rows are \(x\) and columns are \(y\), so the horizontal axis of the image is \(y\).
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(7, 4))
im = ax.imshow(
uv[1],
interpolation="bilinear",
origin="lower",
extent=[0, length_y, 0, length_x],
)
ax.set_xlabel("y")
ax.set_ylabel("x")
plt.colorbar(im, ax=ax, label="v(x, y)")
plt.show()Animate the Simulation
Run this code as a Python script, not in a notebook, so the animation window updates smoothly.
Move the 2D Euler loop into the animation callback. Advance several steps per frame, reapply the Neumann boundaries, and update the image.
import matplotlib.animation as animation
import matplotlib.pyplot as plt
steps_per_frame = 200
fig, ax = plt.subplots(figsize=(7, 4))
im = ax.imshow(uv[1], origin="lower", extent=[0, length_y, 0, length_x], vmin=1, vmax=3.5)
def update_frame(_):
# TODO: advance uv by steps_per_frame Euler steps
# TODO: reapply Neumann boundary conditions after each step
im.set_array(uv[1])
return (im,)
ani = animation.FuncAnimation(fig, update_frame, interval=1, blit=True)
plt.show()global is needed because the callback reassigns uv. Inside a wrapping function, use nonlocal instead.
def update_frame(_):
global uv
for _ in range(steps_per_frame):
uv = uv + dt * gierer_meinhardt_pde_2d(0, uv, d=30, dx=dx)
uv = apply_boundary_conditions(uv, bc="neumann")
im.set_array(uv[1])
return (im,)Reference code is available in amlab/pdes_2d/gierer_meinhardt_2d.py.