递归动画matplotlib

Recursive animation matplotlib

我想为波函数的一些时间传播设置动画。但是我不想每次都计算所有的时间步长,因为它需要大量的时间,而是将波函数的前一个值作为初始值。我不知道如何用 animation.FuncAnimation 实现这一点。 这就是我的想法:

import numpy as np
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt 

wavefunction_0 = some array 

def next_wavefunction(wavefunction_init): 
    wavefunction = time_propagation(Psi = wavefunction_init)
    return wavefunction

def animate(framenumber, wavefunction, surf):
    if framenumber == 0:
        wavefunction = wavefunction_0
    else: 
        wavefunction = next_wavefunction(wavefunction)
    ax.clear()
    surf = ax.plot_surface(X, Y, np.reshape(np.abs(wavefunction), (space_shape)), rstride=1, cstride=1, linewidth=0,  antialiased=False, cmap='jet', edgecolor='none')
    return surf, wavefunction

anim = animation.FuncAnimation(fig, animate, fargs=(wavefunction, surf),
                              interval=200, blit=False)

目前它不工作,因为 fargs = wavefunction 但波函数是 return 值 animate(...)。是否可以获取 animate 的 return 值并将其作为 fargs 传递?

Matplotlib 期望 animate 函数传递给 matplotlib.animation.FuncAnimation return 是一个 artists 的列表,因此不可能(至少在我看来) return 非艺术家喜欢

return surf, wavefunction

因此,即使您将 wavefunction 传递给 animate,您也无法 return 变异数组。除非您可以将代码重构为可以在没有前一帧信息的情况下计算当前帧数组的方式,否则您不能使用这种方法。

有两种方法可以解决这个问题,一种是使用全局变量来存储波函数数组并根据需要对其进行变异,这样在函数中所做的更改会在函数结束后持续存在。为了演示,这里有一个这个实现的例子,它比 3 维的变化波函数稍微简单一些,

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation

n = 100
wf = np.zeros((n,2))

def next_wf():
    global wf
    offset = wf[0,0] + 0.1
    wf[:,0] = np.linspace(offset, np.pi*4+offset, wf.shape[0])
    wf[:,1] = np.sin(wf[:,0])

def animate(frame):
    next_wf()
    plt.cla()
    plot, = plt.plot(wf[:,0], wf[:,1])
    return plot,

next_wf()
fig, ax = plt.subplots(1)
anim = animation.FuncAnimation(fig, animate, interval=25)

这将创建如下动画

但是,应该注意的是,文档的 Variables and Scope 页面明确建议不要使用全局变量,

Note that it is usually very bad practice to access global variables from inside functions, and even worse practice to modify them. This makes it difficult to arrange our program into logically encapsulated parts which do not affect each other in unexpected ways. If a function needs to access some external value, we should pass the value into the function as a parameter. [...]

在一个简单的、自包含的脚本中,它不太可能造成伤害,但在更复杂的代码中,它可能是有害的。更 'proper' 的方法是将整个内容包装在 class 中,即

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation

class waveanim:
    def __init__(self):
        n = 100
        self.wf = np.zeros((n,2))
        self.next_wf()
        fig, ax = plt.subplots(1)
        anim = animation.FuncAnimation(fig, self.animate, interval=25, blit=True)

        anim.save('./animation.gif', writer='imagemagick')

    def next_wf(self):
        offset = self.wf[0,0] + 0.1
        self.wf[:,0] = np.linspace(offset, np.pi*4+offset, self.wf.shape[0])
        self.wf[:,1] = np.sin(self.wf[:,0])

    def animate(self, frame):
        self.next_wf()
        plt.cla()
        plot, = plt.plot(self.wf[:,0], self.wf[:,1])
        return plot,

waveanim()

结果与上面相同。