0

It seems like the documentation for something like this is very much incomplete, or I am just altogether looking in the wrong spot. Previous questions seem to have never been answered or seem different enough not to apply here. My hope is that a solution will provide documentation for this process that does not currently exist. (Edit How wrong I was - the answer more or less already existed at one of the linked posts.)

I have a few hundred nx x ny arrays containing iterations of solutions of a PDE over a grid.

The expected behavior is to create an animation of the 3d surface plots over time (where obviously iterations are the frames). I get a surface plot of the first solution, however after that I am getting a qt-related error and there is no animation.

Here is a minimal working example of the behavior

# matplotlib stuff
from mpl_toolkits.mplot3d import axes3d, Axes3D
import matplotlib.pyplot as plt
from matplotlib import animation

# lots of array indexing will be had
import numpy

# gussy up matplotlib
from matplotlib import cm

def data_generating_function(p, epsilon, it):
# scale values down randomly until epsilon
    pn = numpy.empty_like(p)
    pt = [p.copy()]
    l2norm = 1
    while l2norm > epsilon:
        pn = p.copy()
        p = numpy.random.uniform() * pn
        l2norm = numpy.sqrt(numpy.sum((p - pn)**2) / numpy.sum(pn**2))
        pt = numpy.append(pt, [p], axis=0)
        it += 1
    return pt

def update_plot(i, data, plot):
    ax.clear()
    plot = ax.plot_surface(xv, yv, data[i,:], rstride=1, cstride=1, cmap=cm.plasma, linewidth=0, antialiased=True)
    return plot,

##
# main
##
nx = 200
ny = 100
epsilon = 1e-8
it = 0

# initialize
p = numpy.random.rand(ny, nx)
x = numpy.linspace(0, 1, nx)
y = numpy.linspace(0, 1, ny)

xv, yv = numpy.meshgrid(x, y)

# attach 3D axis to the figure
fig = plt.figure()
ax = Axes3D(fig)

# populate data
# data will contain several hundred nx x ny arrays
data = data_generating_function(p.copy(), epsilon, it)

# set the axes properties
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_zlabel('$z$')
ax.view_init(30, 45)

# create the animation object
plot = ax.plot_surface(xv, yv, data[0,:], rstride=1, cstride=1, cmap=cm.plasma, linewidth=0, antialiased=True)
line_ani = animation.FuncAnimation(fig, update_plot, frames=it, fargs=(data, plot), interval=30, blit=False)

plt.show()

and here is the traceback

Traceback (most recent call last):
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\backends\backend_qt5agg.py", line 197, in __draw_idle_agg
    FigureCanvasAgg.draw(self)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py", line 464, in draw
    self.figure.draw(self.renderer)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\figure.py", line 1150, in draw
    self.canvas.draw_event(renderer)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\backend_bases.py", line 1815, in draw_event
    self.callbacks.process(s, event)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\cbook.py", line 549, in process
    proxy(*args, **kwargs)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\cbook.py", line 416, in __call__
    return mtd(*args, **kwargs)
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\animation.py", line 831, in _start
    self._init_draw()
  File "C:\Users\Walter\Anaconda3\lib\site-packages\matplotlib\animation.py", line 1490, in _init_draw
    self._draw_frame(next(self.new_frame_seq()))
StopIteration

Edit

I should mention that I happen to know that the solutions in data are "correct" (i.e. have the data I expect in the form I expect), and I know that the plots should work out because I can plot any given iteration as a surface by itself (not animated.)

A similar question was asked here but appears never to have been answered.

Edit Edit

Included an example data_generating_function to turn my code snippet into a minimal working example and clarified the expected behavior (in response to comment).

Edit #3

I figured out that I was erroneously assuming that variable "it" was being passed by reference. Changing the code to use "len(data)" instead fixed the problem. If anyone knows why this causes the error that is given, I am curious enough to want to know.

Community
  • 1
  • 1
TrivialCase
  • 1,060
  • 2
  • 14
  • 27
  • Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the **shortest code necessary to reproduce** it in the question itself. See: How to create a [mcve]. In your question, the data is missing and especially `it` is undefined, which could be the source of the problem. The linked question actually has an answer! For another working solution see [this question](http://stackoverflow.com/questions/41744417/animation-of-a-3d-surface-from-calculated-matrices) – ImportanceOfBeingErnest Feb 27 '17 at 19:17

1 Answers1

1

As you found out, the frames argument must be an integer providing the number of frames or a list or iterable, providing the argument to the animation function.

In your case you used the variable it (frames=it). In case it is zero, it = 0, the animation will consist of zero frames and therefore immediately stop, throwing the above error.

A minimal example to reproduce this error is

import matplotlib.pyplot as plt
import matplotlib.animation

fig,ax = plt.subplots()
l, = ax.plot([],[])
x=[];y=[]

def animate(i):
    x.append(i)
    y.append(i**2/9.)
    l.set_data(x,y)
    ax.set_xlim(min(x), max(x)+1.e-5)
    ax.set_ylim(min(y), max(y)+1.e-5)

ani = matplotlib.animation.FuncAnimation(fig, animate, frames=0, interval=500)
plt.show()

Setting frames = 10 gets rid of the error.

Be aware of the fact that even in your modified version of the code. it is constantly zero. In python the concepts of 'by-reference' or 'by-value' are not directly applicable; instead there are mutable and immutable objects. Integers, like it are immutable, therefore changing it in the enclosing scope of the function definition, does not change it outside.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712