递归动画Matplotlib

3

我希望能够用动画展示波函数的时间演化,但又不想每次都计算所有时间步骤,因为这样会花费大量时间,而是要将上一个波函数值作为初始值。我不知道如何使用 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但是animate(...)的返回值是wavefunction。是否可以将animate的返回值作为fargs传递?


你有机会尝试我回答中的任一解决方案吗? - William Miller
@WilliamMiller 我会在下周之前看一下它! - clearseplex
你有时间看这个了吗? - William Miller
1个回答

2

Matplotlib希望在传递给matplotlib.animation.FuncAnimationanimate函数中返回一个artists列表,因此(至少在我理解的范围内)不可能返回非artist类型的内容。

return surf, wavefunction

即使您将wavefunction传递给animate,也无法返回已变异的数组。除非您可以将代码重构为这样一种方式,即可以计算当前帧的数组而不需要来自前一个帧的信息,否则您无法使用此方法。
有两种方法可以解决这个问题,一种是使用全局变量存储wavefunction数组,并根据需要进行变异,这样在函数结束后对其所做的更改就会持续存在。以下是一个比三维波函数更简单的实现示例。
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)

这将创建如下所示的动画:

enter image description here

然而,需要注意的是,在文档的变量和作用域页面中,明确建议不要使用全局变量:

请注意,从函数内部访问全局变量通常是非常糟糕的做法,修改它们就更糟了。这使得我们很难将程序组织成逻辑上封装的部分,这些部分不会以意想不到的方式相互影响。如果函数需要访问某些外部值,我们应该将该值作为参数传递给函数。[...]

在一个简单、自包含的脚本中,这样做可能不会造成太大的影响,但在更复杂的代码中,它可能会对代码产生负面影响。更“正确”的做法是将整个代码块包装在一个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()

这与上面的结果相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接