0
votes

I am facing issues with the implementation of the method animation from the class wave bellow. The animation method aims to create an animation of a wave propagation. I do not want that the animation method modify the state instance variable u of the Wave object, and because of that I am trying to create a local variable u inside the method animation. Nevertheless, I do not know how to pass the local variable u from the animation method for the animate function that is defined inside the animation method. In the way that I tried to implement, from my conception, the local variable u of the method animation should be a king of global variable for the animate method (that is defined inside the animation method). But, this assumption is cleary wrong, otherwise I would not get an error. As a complementary information the error I am getting is: UnboundLocalError: local variable 'u' referenced before assignment. I will be glad if someone indicate to me the way for the implementation that I want.

thanks in advance

class Wave(object):


    def __init__(self, phenomenon):
        '''
        nx: Number of spatial grid points
        dx: Distance between any pair of adjacent grid points
        nt: Number of steps in time
        nu: Diffusion coefficient
        dt: Value of the step in time
        c: wave velocity
        u: grid vector of the wave
        '''

        if phenomenon == 'convection':
            self.phenomenon = phenomenon
            self.nx = 81
            self.dx = 2.0/(self.nx - 1) # Distance between any pair of adjacent grid
            self.nt = 100
            self.dt = 0.002
            self.c = 3

            self.x = numpy.linspace(0,4,self.nx)
            self.u =  numpy.ones(self.nx)
            self.lbound = numpy.where(self.x >= 0.5)
            self.ubound = numpy.where(self.x <= 1.0)
            self.bounds = numpy.intersect1d(self.lbound[0], self.ubound[0])
            self.u[self.bounds] = 2

        if phenomenon == 'diffusion':
            ...

        if phenomenon == 'burgers':
            ...


    def _convection(self, u):
        un = u.copy()
        u[1:] = un[1:] - self.c*self.dt/self.dx*(un[1:] - un[:-1])
        u[0] = 1.0
        return u


    def integration(self):
        if self.phenomenon == 'convection':
            for n in range(1,self.nt):
                self.u = self._convection(u=self.u)

        if self.phenomenon == 'diffusion':
            ...

        if self.phenomenon == 'burgers':
            ...


    def animation(self):
        fig = plt.figure()
        ax = plt.axes(xlim=(0,4), ylim=(0,3))
        ax.grid()

        line, = ax.plot([], [], 'o-', lw=2)
        time_template = 'time = %.2fs'
        time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)

        def init():
            line.set_data([], [])
            time_text.set_text('')
            return line, time_text

        x = self.x
        u = self.u.copy()
        def animate(i):

            if self.phenomenon == 'convection':
                u = self._convection(u=u)

            if self.phenomenon == 'diffusion':
                ...    
            if self.phenomenon == 'burgers':
                ...

            line.set_data(x,u)
            time_text.set_text(time_template % (i*self.dt))
            return line, time_text

        anim = animation.FuncAnimation(fig, animate, frames=500, init_func=init, interval=10, blit=True)
        plt.show()

EDIT

Complete trace error:

Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in call return self.func(*args) File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 536, in callit func(*args) File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer TimerBase._on_timer(self) File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer ret = func(*args, **kwargs) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 876, in _step still_going = Animation._step(self, *args) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 735, in _step self._draw_next_frame(framedata, self._blit) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 754, in _draw_next_frame self._draw_frame(framedata) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 1049, in _draw_frame self._drawn_artists = self._func(framedata, *self._args) File "wave.py", line 201, in animate un = u.copy() UnboundLocalError: local variable 'u' referenced before assignment Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in call return self.func(*args) File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 536, in callit func(*args) File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer TimerBase._on_timer(self) File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer ret = func(*args, **kwargs) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 876, in _step still_going = Animation._step(self, *args) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 735, in _step self._draw_next_frame(framedata, self._blit) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 754, in _draw_next_frame self._draw_frame(framedata) File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 1049, in _draw_frame self._drawn_artists = self._func(framedata, *self._args) File "wave.py", line 201, in animate un = u.copy() UnboundLocalError: local variable 'u' referenced before assignment

1
Can we have the complete stack trace ? - aluriak
Can we have the complete stack trace in a format that's possible to read? - kindall

1 Answers

2
votes

When you capture a variable with a closure in Python, you are not allowed to assign to it. If Python sees you assign to it, then it isn't allowed to be captured from the enclosing scope; it must be a new function-local variable (local to your animate function). But since you're trying to use the captured u to initialize it, you need that u's value, but in this context Python has decided that u must be local to animate, so it isn't looking at animate's enclosing scope. That's why you're getting that error.

A simple way to get around that in your case is just to define animate as

def animate(i, u=u):
    ...

This explicitly passes a copy of u into animate.