Matplotlib animation works on Windows and Linux, but not on Mac OS

I’ve stumbled upon a weird problem - matplotlib animation in my Python script works fine on Windows and Linux, but doesn’t show up on Mac OS. Here’s a video demonstration.

The script source code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import math

# define x coordinates
x0, x1 = 0, 1
n = 500
x = np.linspace(x0, x1, n)
dx = x[1] - x[0]

# define time coordinates
t0, t1 = 0, 1
m = 500
t = np.linspace(t0, t1, m)
dt = t[1] - t[0]

# define velocity
uleft, uright = 1.0, 1.0
c = np.linspace(uleft, uright, x.shape[0])

# calculate stability
stab = np.amax(c) * dt / dx
print(f"stability r: {stab}")

# position in x
xs = 0.5
# width of the wavelet
rick = 0.1
xxsq = np.square((x - xs) / rick)
r = (1 - 12 * np.pi * xxsq) * np.exp(-6 * np.pi * xxsq)

# define two subplots
fig, ax = plt.subplots(2)

# plot ricker wavelet
ax[0].plot(x,r)
ax[0].set(
    xlabel="x",
    ylabel="displacement",
    title="initial displacement, Ricker wavelet",
    ylim=[-1.0, 1.0]
)

# initial displacement
u0 = r
# make sure boundary conditions are fulfilled
u0[0], u0[-1] = 0, 0
# initial velocity
ut0 = np.zeros(n)
# estimate second space derivative at t = 0
uxx0 = np.concatenate((u0, np.zeros(2))) \
    - 2 * np.concatenate((np.zeros(1), u0, np.zeros(1))) \
    + np.concatenate((np.zeros(2), u0))
# estimate the first time step
u1 = u0 + dt * ut0 + dt * dt / 2 / dx / dx * c * c * uxx0[1:-1]
# initialize u with zeros
u = np.zeros(n)

# define matrix to make all spatial derivatives at once
A = np.diag(2 * np.ones(n - 2)) \
    + np.diag(-np.ones(n - 3), -1) \
    + np.diag(-np.ones(n - 3), 1)
A = dt * dt / dx / dx * A

# the free inner points where no boundary conditions are prescribed
free = np.linspace(1, n - 2, n - 2, dtype="int")
# velocity squared
c2 = c * c
c2 = c2[free]

# time loop
tmp0 = u0[free]  # first time step
tmp1 = u1[free]  # second time step

#prepare the subplot for the displacement
ax[1].set(
    ylim=(-1.0, 1.0),
    xlabel="x",
    ylabel="displacement",
    title="explicit fd solution of 1d wave eqn"
)


def animate(it):
    global tmp0, tmp1
    # predict u in the next time step
    u[free] = 2 * tmp1 - tmp0 - c2 * np.dot(A, tmp1)
    # update the previous time steps
    tmp0 = tmp1
    tmp1 = u[free]
    line, = ax[1].plot(x, u)
    return line,


# total time of animation in ms
totaltime = 1000
tint = totaltime / m
ani = animation.FuncAnimation(
    fig,
    animate,
    np.arange(m),
    interval=tint,
    blit=True,
    repeat=False
)
plt.show()

On all 3 systems (Windows, Linux, Mac OS) I only installed matplotlib and numpy, so the environment is seemingly the same.

Environments
# Windows (10.19041.450, x64)

- Python 3.7.2
- matplotlib 3.3.1
- numpy 1.19.1

# Linux (Ubuntu 20.04.1, x64)

- Python 3.8.2
- matplotlib 3.3.1
- numpy 1.19.1

# Mac OS (10.15.6, x64)

- Python 3.8.5
- matplotlib 3.3.1
- numpy 1.19.1

I’ve tried some animation examples from documentation, and they work fine on Mac OS, so it should be something about my particular script.

Do have any guess what could be different in my case and how to make my animation work on Mac OS too?

I’ve also posted this question on Stack Overflow.

I’ve discovered that in my case the default matplot backend is MacOSX, and apparently this one is not capable of doing my animation (for whatever reasons).

And the backend that is capable turned out to be TkAgg. So after setting it like this:

matplotlib.use("TkAgg")

I now get the animation in my script on Mac OS too.

By the way, first I tried setting it via rcParams, but that didn’t have any effect:

plt.rcParams["backend"] = "TkAgg"

If you are interested in more details, I wrote a blog post about the whole thing.

Sorry for the confusion.

We only consult the rcparams['backend'] when importing pyplot so changing it after the import has no effect (you also used to have to use mpl.use before you imported pyplot, but we fixed that a few versions ago). See https://matplotlib.org/tutorials/introductory/usage.html?highlight=backend#backends for more details.

Can you please open a bug report for the script that did not work correctly on OSX?

1 Like

Oh, I see. Indeed, putting matplotlib.rcParams["backend"] = "TkAgg" before import matplotlib.pyplot does the trick too, but then my linter become unhappy about imports not being at the top of the file, so I’ll stick with matplotlib.use("TkAgg") after all the imports (good thing it works like this now).

Sure thing, here’s the bug-report.