I'm trying to recreate Comway's game of life from this website: https://www.geeksforgeeks.org/conways-game-life-python-implementation/
And I'm facing an issue with the next section of the code:
def main():
# Command line args are in sys.argv[1], sys.argv[2] ..
# sys.argv[0] is the script name itself and can be ignored
# parse arguments
parser = argparse.ArgumentParser(description="Runs Conway's Game of Life simulation.")
# add arguments
parser.add_argument('--grid-size', dest='N', required=False)
parser.add_argument('--mov-file', dest='movfile', required=False)
parser.add_argument('--interval', dest='interval', required=False)
parser.add_argument('--glider', action='store_true', required=False)
parser.add_argument('--gosper', action='store_true', required=False)
args, unknown = parser.parse_known_args()
# args = parser.parse_args()
# set grid size
N = 100
if args.N and int(args.N) > 8:
N = int(args.N)
# set animation update interval
updateInterval = 50
if args.interval:
updateInterval = int(args.interval)
# declare grid
grid = np.array([])
# check if "glider" demo flag is specified
if args.glider:
grid = np.zeros(N*N).reshape(N, N)
addGlider(1, 1, grid)
elif args.gosper:
grid = np.zeros(N*N).reshape(N, N)
addGosperGliderGun(10, 10, grid)
else: # populate grid with random on/off -
# more off than on
grid = randomGrid(N)
%matplotlib notebook
# set up animation
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(fig, update, fargs=(img, grid, N, ),
frames = 10,
interval=updateInterval,
save_count=50)
# # of frames?
# set output file
if args.movfile:
ani.save(args.movfile, fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
# call main
if __name__ == '__main__':
main()
I already did some changes to the code since I'm working on Jupyter Notebook and parser arguments were not working as initially stated.
The idea of this exercise is to generate an animation. Nonetheless, the animation is not working and it just shows a sort of .png image which is not animated.
As per this response, I should add the next line (which I did):
%matplotlib tk
And it generates an interactive backend with the following options: home, back, forward, pan, zoom and download buttons. However, they are not working either. All the available frames show the same image and download button shows an empty tab.
So, my question is the following: How can this code be improved in order to get the animation working? I think that %matplotlib tk may go anywhere else (like before an specific line) but I have tried some combinations and what I got has been the closest to what I'm looking for.
Pd: I also looked at this response, but this approach didn't work.
I try this, and run:
%matplotlib tk
N = 100
updateInterval = 50
grid = randomGrid(N)
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(
fig,
update,
fargs=(img, grid, N, ),
frames=10,
interval=updateInterval,
save_count=50
)
plt.show()
I only clean the code removing argparse and add '%matplotlib tk' as you say, in one cell and run.
Edit #1
To help you with your question in comments I write this function.
def makeGrid(grid, text):
for i, line in enumerate(text.splitlines()[1:]):
print(line)
for j, point in enumerate(line):
if point != " ":
grid[i][j] = 255
return grid
Try this:
%matplotlib tk
N = 50
updateInterval = 50
grid = randomGrid(N)
grid.fill(0)
grid = makeGrid(grid, '''
x
x x
xx xx xx
x x xx xx
xx x x xx
xx x x xx x x
x x x
x x
xx
''')
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(
fig,
update,
fargs=(img, grid, N, ),
frames=10,
interval=updateInterval,
save_count=50
)
plt.show()
Edit #2
I like this, and I update how to modify the grid...
gliderstr = '''
--x
x-x
-xx
'''
gunstr = '''
------------------------x
----------------------x-x
------------xx------xx------------xx
-----------x---x----xx------------xx
xx--------x-----x---xx
xx--------x---x-xx----x-x
----------x-----x-------x
-----------x---x
------------xx
'''
def fill_grid(grid, iterable, value=255):
for x, y in iterable:
grid[x][y] = value
return grid
def iter_text(text, accept='x', x=0, y=0):
for i, line in enumerate(text.splitlines()):
for j, ch in enumerate(line):
if ch == accept:
yield i + x, j + y
Use like:
%matplotlib tk
N = 100
update_interval = 50
grid = make_grid(N)
fill_grid(grid, iter_text(gunstr, 20, 20))
fill_grid(grid, iter_text(gliderstr, 0, 0))
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(
fig,
update,
fargs=(img, grid, N, ),
frames=10,
interval=update_interval,
save_count=50
)
plt.show()
Related
I'm trying to plot data that is generated in runtime. In order to do so I'm using matplotlib.animation.FuncAnimation.
While the data is displayed correctly, the axis values are not updating accordingly to the values that are being displayed:
The x axis displays values from 0 to 10 eventhough I update them in every iteration in the update_line function (see code below).
DataSource contains the data vector and appends values at runtime, and also returns the indexes of the values being returned:
import numpy as np
class DataSource:
data = []
display = 10
# Append one random number and return last 10 values
def getData(self):
self.data.append(np.random.rand(1)[0])
if(len(self.data) <= self.display):
return self.data
else:
return self.data[-self.display:]
# Return the index of the last 10 values
def getIndexVector(self):
if(len(self.data) <= self.display):
return list(range(len(self.data)))
else:
return list(range(len(self.data)))[-self.display:]
I've obtained the plot_animation function from the matplotlib docs.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from datasource import DataSource
def update_line(num, source, line):
data = source.getData()
indexs = source.getIndexVector()
if indexs[0] != 0:
plt.xlim(indexs[0], indexs[-1])
dim=np.arange(indexs[0],indexs[-1],1)
plt.xticks(dim)
line.set_data(indexs,data)
return line,
def plot_animation():
fig1 = plt.figure()
source = DataSource()
l, = plt.plot([], [], 'r-')
plt.xlim(0, 10)
plt.ylim(0, 1)
plt.xlabel('x')
plt.title('test')
line_ani = animation.FuncAnimation(fig1, update_line, fargs=(source, l),
interval=150, blit=True)
# To save the animation, use the command: line_ani.save('lines.mp4')
plt.show()
if __name__ == "__main__":
plot_animation()
How can I update the x axis values in every iteration of the animation?
(I appreciate suggestions to improve the code if you see any mistakes, eventhough they might not be related to the question).
Here is a simple case of how you can achieve this.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
%matplotlib notebook
#data generator
data = np.random.random((100,))
#setup figure
fig = plt.figure(figsize=(5,4))
ax = fig.add_subplot(1,1,1)
#rolling window size
repeat_length = 25
ax.set_xlim([0,repeat_length])
ax.set_ylim([-2,2])
#set figure to be modified
im, = ax.plot([], [])
def func(n):
im.set_xdata(np.arange(n))
im.set_ydata(data[0:n])
if n>repeat_length:
lim = ax.set_xlim(n-repeat_length, n)
else:
lim = ax.set_xlim(0,repeat_length)
return im
ani = animation.FuncAnimation(fig, func, frames=data.shape[0], interval=30, blit=False)
plt.show()
#ani.save('animation.gif',writer='pillow', fps=30)
Solution
My problem was in the following line:
line_ani = animation.FuncAnimation(fig1, update_line, fargs=(source, l),
interval=150, blit=True)
What I had to do is change the blit parameter to False and the x axis started to move as desired.
I've got a script wherein I have two functions, makeplots() which makes a figure of blank subplots arranged in a particular way (depending on the number of subplots to be drawn), and drawplots() which is called later, drawing the plots (obviously). The functions are posted below.
The script does some analysis of data for a given number of 'targets' (which can number anywhere from one to nine) and creates plots of the linear regression for each target. When there are multiple targets, this works great. But when there's a single target (i.e. a single 'subplot' in the figure), the Y-axis label overlaps the axis itself (this does not happen when there are multiple targets).
Ideally, each subplot would be square, no labels would overlap, and it would work the same for one target as for multiple targets. But when I tried to decrease the size of the y-axis label and shift it over a bit, it appears that the actual axes object was drawn over the previously blank, square plot (whose axes ranged from 0 to 1), and the old tick mark labels are still visible. I'd like to have those old tick marks removed when calling drawplots(). I've tried changing the subplot_kw={} arguments in makeplots, as well as removing ax.set_aspect('auto') from drawplots, both to no avail. Note that there are also screenshots of various behaviors at the end, also.
def makeplots(targets, active=actwindow):
def rowcnt(y):
rownumb = y//3 if (y%3 == 0) else y//3+1
return rownumb
def colcnt(x):
if x <= 3: colnumb = x
elif x == 4: colnumb = 2
else: colnumb = 3
return colnumb
numsubs = len(targets)
numrow, numcol = rowcnt(numsubs), colcnt(numsubs)
if numsubs >= 1:
if numsubs == 1:
fig, axs = plt.subplots(num='LOD-95 Plots', nrows=1, ncols=1, figsize = [8,6], subplot_kw={'adjustable': 'box', 'aspect': 1})
# changed 'box' to 'datalim'
fig, axs = plt.subplots(num='LOD-95 Plots', nrows=numrow, ncols=numcol, figsize = [numcol*6,numrow*6], subplot_kw={'adjustable': 'box', 'aspect': 1})
fig.text(0.02, 0.5, 'Probit score\n $(\sigma + 5)$', va='center', rotation='vertical', size='16')
else:
raise ValueError(f'Error generating plots [call: makeplots({targets},{active}) - invalid numsubs value]')
axs = np.ravel(axs)
for i, ax in enumerate(axs):
ax.set_title(f'Limit of Detection: {targets[i]}', size=11)
ax.grid()
return fig, axs
and
def drawplots(ax, dftables, color1, color2):
y = dftables.probit
y95 = 6.6448536269514722
logreg = False
regfun = lambda m, x, b : (m*x) + b
regq = scipy.stats.linregress(dftables.qty,y)
regl = scipy.stats.linregress(dftables.log_qty,y)
if regq.rvalue**2 >= regl.rvalue**2:
regression = regq
x_label = 'input quantity'
x = dftables.qty
elif regq.rvalue**2 < regl.rvalue**2:
regression = regl
x_label = '$log_{10}$(input quantity)'
x = dftables.log_qty
logreg = True
slope, intercept, r = regression.slope, regression.intercept, regression.rvalue
r2 = r**2
lod = (y95-intercept)/slope
xr = [0, lod*1.2]
yr = [intercept, regfun(slope, xr[1], intercept)]
regeqn = "y = "+str(f"{slope:.2e}")+"x + "+str(f"{intercept:.3f}")
if logreg:
lodstr = f'log(LOD) = {lod:.2f}' if lod <= 100 else f'log(LOD) = {lod:.2e}'
elif not logreg:
lodstr = f'LOD = {lod:.2f}' if lod <= 100 else f'LOD = {lod:.2e}'
# raise ValueError(f'Error raised calling drawplots()')
ax.set_xlabel(x_label, fontweight='bold')
ax.plot(xr, yr, color=color1, linestyle='--') # plot regression line
ax.plot(lod, y95, marker='D', color=color2, markersize=7) # plot point for LoD
ax.plot(xr, [y95,y95], color=color2, linestyle=':') # horizontal crosshair
ax.plot([lod,lod],[0, 7.1], color=color2, linestyle=':') # vertical crosshair
ax.scatter(x, y, s=81, color=color1, marker='.') # actual data points
ax.annotate(f"{lodstr}", xy=(lod,0.1),
xytext=(0.9*lod,0.5), fontsize=8, arrowprops = dict(facecolor='black', headlength=5, width=2, headwidth=5))
ax.set_aspect('auto')
ax.set_xlim(left=0)
ax.set_ylim(bottom=0)
ax.plot()
if logreg: lod = 10 ** lod
return r2, lod, regeqn, logreg
The context they're called in:
fig, axs = makeplots(targets)
wg.SetForegroundWindow(actwindow)
with open(outName, 'a+') as f:
print(f"Lower Limit of Detection Analysis on {dt} at {tm}\n", file=f)
for i, tars in enumerate(targets):
data[tars] = stripThousands(data[tars])
# logans = checkyn(f"Analyze {tars} using log10(concentration/quantity)? (y/n): ")
for idx, val in enumerate(qtys):
tables[i,idx,2] = hitrate(val,data,tars)
tables[i,idx,3] = norm.ppf(tables[i,idx,2])+5
printtables[tars] = pd.DataFrame(tables[i,:,:], columns=["qty","log_qty","probability","probit"])
# construct dataframes from np.arrays and drop
# rows with infinite probit values:
dftables[tars] = pd.DataFrame(tables[i,:,:], columns=["qty","log_qty","probability","probit"])
dftables[tars].probit.replace([np.inf,-np.inf],np.nan, inplace=True)
dftables[tars].dropna(inplace=True)
r2, lod, eqn, logreg = drawplots(axs[i], dftables[tars], cbcolors[i], cbcolors[i+5])
You should clear the axes in each iteration using pyplot.cla().
You posted a lot of code, so I'm not 100% sure of the best location to place it in your code, but the general idea is to clear the axes before each new plot.
Here is a minimal demo without cla():
x = [[1,2,3], [3,2,1]]
fig, ax = plt.subplots()
for index, data in enumerate(x):
ax.plot(data)
And with cla():
for index, data in enumerate(x):
ax.cla()
ax.plot(data)
I'm using the following code to produce an animation with matplotlib that is intended to visualize my experiments.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation, PillowWriter
plt.rcParams['animation.html'] = 'jshtml'
def make_grid(X, description=None, labels=None, title_fmt="label: {}", cmap='gray', ncols=3, colors=None):
L = len(X)
nrows = -(-L // ncols)
frame_plot = []
for i in range(L):
plt.subplot(nrows, ncols, i + 1)
im = plt.imshow(X[i].squeeze(), cmap=cmap, interpolation='none')
if labels is not None:
color = 'k' if colors is None else colors[i]
plt.title(title_fmt.format(labels[i]), color=color)
plt.xticks([])
plt.yticks([])
frame_plot.append(im)
return frame_plot
def animate_step(X):
return X ** 2
n_splots = 6
X = np.random.random((n_splots,32,32,3))
Y = X
X_t = []
for i in range(10):
Y = animate_step(Y)
X_t.append((Y, i))
frames = []
for X, step in X_t:
frame = make_grid(X,
description="step={}".format(step),
labels=range(n_splots),
title_fmt="target: {}")
frames.append(frame)
anim = ArtistAnimation(plt.gcf(), frames,
interval=300, repeat_delay=8000, blit=True)
plt.close()
anim.save("test.gif", writer=PillowWriter())
anim
The result can be seen here:
https://i.stack.imgur.com/OaOsf.gif
It works fine so far, but I'm having trouble getting a shared xlabel to add a description for all of the 6 subplots in the animation. It is supposed to show what step the image is on, i.e. "step=5".
Since it is an animation, I cannot use xlabel or set_title (since it would be constant over the whole animation) and have to draw the text myself.
I've tried something along the lines of..
def make_grid(X, description=None, labels=None, title_fmt="label: {}", cmap='gray', ncols=3, colors=None):
L = len(X)
nrows = -(-L // ncols)
frame_plot = []
desc = plt.text(0.5, .04, description,
size=plt.rcparams["axes.titlesize"],
ha="center",
transform=plt.gca().transAxes
)
frame_plot.append(desc)
...
This, of course, won't work, because the axes are not yet created. I tried using the axis of another subplot(nrows, 1, nrows), but then the existing images are drawn over..
Does anyone have a solution to this?
Edit:
unclean, hacky solution for now:
Wait for the axes of the middle image of the last row to be created and use that for plotting the text.
In the for loop:
...
if i == int((nrows - 0.5) * ncols):
title = ax.text(0.25, -.3, description,
size=plt.rcParams["axes.titlesize"],
# ha="center",
transform=ax.transAxes
)
frame_plot.append(title)
...
To me, your case is easier to solve with FuncAnimation instead of ArtistAnimation, even if you already have access to the full list of data you want to show animated (see this thread for a discussion about the difference between the two functions).
Inspired from this FuncAnimation example, I wrote the code below that does what you needed (using the same code with ArtistAnimation and correct list of arguments does not work).
The main idea is to initialize all elements to be animated at the beginning, and to update them over the animation frames. This can be done for the text object (step_txt = fig.text(...)) in charge of displaying the current step, and for the images out from ax.imshow. You can then update whatever object you would like to see animated with this recipe.
Note that the technique works if you want the text to be an x_label or any text you choose to show. See the commented line in the code.
#!/Users/seydoux/anaconda3/envs/jupyter/bin/python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter
# parameters
n_frames = 10
n_splots = 6
n_cols = 3
n_rows = n_splots // n_cols
def update_data(x):
return x ** 2
# create all snapshots
snapshots = [np.random.rand(n_splots, 32, 32, 3)]
for _ in range(n_frames):
snapshots.append(update_data(snapshots[-1]))
# initialize figure and static elements
fig, axes = plt.subplots(2, 3)
axes = axes.ravel() # so we can access all axes with a single index
for i, ax in enumerate(axes):
ax.set_xticks([])
ax.set_yticks([])
ax.set_title("target: {}".format(i))
# initialize elements to be animated
step_txt = fig.text(0.5, 0.95, "step: 0", ha="center", weight="bold")
# step_txt = axes[4].set_xlabel("step: 0") # also works with x_label
imgs = list()
for a, s in zip(axes, snapshots[0]):
imgs.append(a.imshow(s, interpolation="none", cmap="gray"))
# animation function
def animate(i):
# update images
for img, s in zip(imgs, snapshots[i]):
img.set_data(s)
# update text
step_txt.set_text("step: {}".format(i))
# etc
anim = FuncAnimation(fig, animate, frames=n_frames, interval=300)
anim.save("test.gif", writer=PillowWriter())
Here is the output I got from the above code:
I'm trying to plot the time evolution of a function f(x,t). The data is stored in a file which has the following format:
1st row:f(0,0) f(0,1) f(0,2) ....f(0,N)
2nd row:f(1,0) f(1,1) f(1,2) ....f(1,N)
Mth row:f(M,0) f(M,1) f(M,2) ....f(M,N)
where N is the no: of points of the simulation box and M is the number of timesteps.
I used basic_animation by Jake Vanderplas (https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/) to start with, the original example works fine as long as i put blit=False.
Then i tried to replace x by :
x= np.arange(0,192)
and y by the contents of the file mentioned above.
If i do just plt.plot(x,y), it does plot f(x,t) at a given time t, but I want the animation of f(x,t) in time.
set_data should accept 2 1Darrays and I've checked that len(x)=len(y).
But I get the following error message:
'RuntimeError: xdata and ydata must be the same length'
This is the code (in the future i would like to plot multiple functions):
"""
Modified Matplotlib Animation Example
original example:
email: vanderplas#astro.washington.edu
website: http://jakevdp.github.com
license: BSD
Feel free to use and modify this, but keep the above information.
"""
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from itertools import islice
filename = 'DensityByPropagation__a_0_VHxcS_kick'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 192), ylim=(-2, 2))
lineS, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
w = np.linspace(0, 2, 1000)
z = np.sin(2 * np.pi * (w - 0.01 * i))
x= np.arange(0,192)
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
#plt.plot(x,y)
#plt.show()
#print len(x)
#print len(y)
#lineS.set_data(w,z)
lineS.set_data(x,y)
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=False)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieJoh.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
I'm not sure what exactly is causing your error, but let me point something out, then I'll make a toy example that should help clarify what's happening.
These lines seem unnecessarily complicated.
with open(filename) as fobj:
ketchup = islice(fobj, 0, None, 10)
for line in ketchup:
x,y = x,zip(*([float(y) for y in line.split("\t")] for line in fobj))
If your data is in fact in the simple format you stated, i.e., values separated by spaces, np.loadtxt() would load all the values into an easy to manage array.
Example
Lets assume this is your data file (10 time steps, 2 points on plot at each step):
0 0 0 0 0 0 0 0 0 0
9 8 7 6 5 4 3 2 1 0
Now some code:
filename = 'data.txt'
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 1), ylim=(0, 9))
lineS, = ax.plot([], [], lw=2)
x = range(2) # the domain
Now we load the data in with np.loadtxt(), this creates a 2-d matrix with t in the columns and x in the rows. We then transpose it to make indexing each time step possible in animate().
# load the data from file
data = np.loadtxt(filename)
# transpose so we could easily index in the animate() function
data = np.transpose(data)
Now for animation functions. This part is really quite simple. animate(i) takes one argument - the frame number. Using the frame number, we extract the values of f(x,t=frameNumber) and set that as the data on the plot.
# initialization function: plot the background of each frame
def init():
lineS.set_data([], [])
return lineS,
# animation function. This is called sequentially
def animate(i):
lineS.set_data(x, data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=10, interval=100, blit=True)
plt.show()
This is the working code:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# Input
filename = 'PeriodicDensity' # Filename
x = np.linspace(-7.5,7.5,192) # Domain
xLimits=[-7.5,7.5] # x limits
yLimits=[0,1] # y limits
framesToUse = range(1,9000,150)# The time-steps to plot
# load the data from file
data = np.loadtxt(filename)
# Set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=xLimits,ylim=yLimits)
lineS, = ax.plot([], [], lw=2)
# Initialisation function
def init():
lineS.set_data([],[])
return lineS,
# Animation function (called sequentially)
def animate(i):
lineS.set_data(x,data[i])
return lineS,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,interval=1000, frames=framesToUse, blit=True)
# save the animation as an mp4. This requires ffmpeg or mencoder to be
# installed. The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5. You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('movieDensity.mp4', fps=1, extra_args=['-vcodec', 'libx264'])
plt.show()
I am writing a program for 2D FDTD light propagation, in this code, when I run the program with ax.imshow() command in the animate function, the program works fine whereas when I use the im.set_data() command, it gives me a blank image. Can somebody please tell me what am I doing wrong? Also, can somebody tell me how to set the colormap at the beginning so that I dont have to update it during the animation loop. The point is I don't want the imshow() command to draw everything everytime the loop is run.
Thanks for all the help. I am learning programming please suggest me what to do.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
xdim = 100
ydim = 100
epsilon = np.ones([xdim,ydim])*8.854187817*10**(-12)
mu = np.ones([xdim,ydim])*4*np.pi*10**(-7)
c = 299792458
delta = 10**-6
deltat = delta/(c*(2**0.5))
Hz = np.zeros([xdim,ydim])
Ey = np.zeros([xdim,ydim])
Ex = np.zeros([xdim,ydim])
fig = plt.figure()
ax = plt.axes()
im = ax.imshow(Hz)
Hz[xdim/2,ydim/2]=1
def init():
im.set_data(np.zeros(Hz.shape))
return
def animate(n, *args, **kwargs):
Ex[0:xdim-1,0:ydim-1]=Ex[0:xdim-1,0:ydim-1]+(deltat/(delta*mu[0:xdim-1,0:ydim-1]))*(Hz[1:xdim,0:ydim-1]-Hz[0:xdim-1,0:ydim-1])
Ey[0:xdim-1,0:ydim-1]=Ey[0:xdim-1,0:ydim-1]-(deltat/(delta*mu[0:xdim-1,0:ydim-1]))*(Hz[0:xdim-1,1:ydim]-Hz[0:xdim-1,0:ydim-1])
Hz[1:xdim,1:ydim]=Hz[1:xdim,1:ydim]+(deltat/(delta*epsilon[1:xdim,1:ydim]))*(Ex[1:xdim,1:ydim]-Ex[0:xdim-1,1:ydim]-Ey[1:xdim,1:ydim]+Ey[1:xdim,0:ydim-1])
if(n==0):Hz[xdim/2,ydim/2]=0
#im.set_data(Hz)
ax.imshow(Hz) # Delete this command and try running the program with the above command.
return
ani = animation.FuncAnimation(fig, animate, init_func=init, frames = 200, interval = 10, blit = False, repeat = False)
fig.show()
Actually, your first version was working just fine also. The problem was that because im is initialized with an array of zeros, the vmin and vmax for the colorscale were both zero. Updates to im after that using set_data did not update vmin and vmax, whereas ax.imshow automatically rescales the color ranges. If you set the color ranges at the beginning to something reasonable, it works fine:
ax.imshow(Hz, vmin=-0.2, vmax=0.2)
That's the only thing you need to change from the code in the question to make it work (with im.set_data in the animation function).
I got the program to work by making a few changes, though i cannot understand why it is not working the way i wrote it in the question. Here is what i changed:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
xdim = 100
ydim = 100
epsilon = np.ones([xdim,ydim])*8.854187817*10**(-12)
mu = np.ones([xdim,ydim])*4*np.pi*10**(-7)
c = 299792458
delta = 10**-6
deltat = delta/(c*(2**0.5))
Hz = np.zeros([xdim,ydim])
Ey = np.zeros([xdim,ydim])
Ex = np.zeros([xdim,ydim])
Hz[xdim/2,ydim/2]=1
def init():
global fig, ax, im
fig = plt.figure()
ax = plt.axes()
im = ax.imshow(Hz, cmap="jet")
im.set_data(np.zeros(Hz.shape))
return
def animate(n):
Ex[0:xdim-1,0:ydim-1]=Ex[0:xdim-1,0:ydim-1]+(deltat/(delta*mu[0:xdim-1,0:ydim-1]))*(Hz[1:xdim,0:ydim-1]-Hz[0:xdim-1,0:ydim-1])
Ey[0:xdim-1,0:ydim-1]=Ey[0:xdim-1,0:ydim-1]-(deltat/(delta*mu[0:xdim-1,0:ydim-1]))*(Hz[0:xdim-1,1:ydim]-Hz[0:xdim-1,0:ydim-1])
Hz[1:xdim,1:ydim]=Hz[1:xdim,1:ydim]+(deltat/(delta*epsilon[1:xdim,1:ydim]))*(Ex[1:xdim,1:ydim]-Ex[0:xdim-1,1:ydim]-Ey[1:xdim,1:ydim]+Ey[1:xdim,0:ydim-1])
if(n==0):Hz[xdim/2,ydim/2]=0
im.set_data(Hz)
return
init()
ani = animation.FuncAnimation(fig, animate, frames = 500, interval = 10, blit = False, repeat = False)
fig.show()