pythonpython-3.xnumpyanimationmovie

How to create an animation (mpeg movie, for example) of plots, from a text file, without saving them individually to computer?


so I am working with some output (in a .txt file) from a separate code that takes a final matrix and just writes this matrix to a file. The output has each line of the file being the solution to the 1D heat equation at a certain timestep. This matrix could get up to a several hundred thousand rows long (based on however many timesteps I choose to run). For example, maybe the output looks like this:

1 2 3 4 5 6 7
2 3 4 5 6 7 8
3 4 5 6 7 8 9
4 5 6 7 8 9 10

and I have x-values that I create using numpy.linspace.

My goal is to create a movie (.mpeg, for example) that basically plots plt.plot(x,y), where x is the same in each frame and y is each row of the matrix, starting from the first row and ending at the last row.

In reality, I have 6000 rows and 401 nodes, giving me a 6000 by 401 matrix in an output.txt file, but I expect the matrix to be a lot larger when I increase the timestep in the solver code to possibly 1,000,000 timesteps (which could give me a few hundred thousand rows). Due to the amount of plotting, I am trying to refrain from the method of writing multiple images for each row and storing them on my computer and then compiling them onto one movie - I would like to write this data to a file all at once.

Below is what I have tried so far:

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.animation as manimation


FFMpegWriter = manimation.writers['ffmpeg']
metadata = dict(title='Movie Test', artist='Matplotlib',
                comment='Movie support!')
writer = FFMpegWriter(fps=15, metadata=metadata)

fig = plt.figure()
l, = plt.plot([], [], 'k-o')

solverlist = ["explicit", "implicit", "crank-nicolson"]
filename = f"{solverlist[2]}-solver/cn_output_400_nodes.txt"
loaded_matrix = np.loadtxt(filename, dtype='f', delimiter=' ')

with writer.saving(fig, f"{solverlist[2]}_400_node_solution.mp4", 100):    
    x = np.linspace(0.0, 2.0, len(loaded_matrix[1]))
    for i in range(len(loaded_matrix)):
        y = loaded_matrix[i]
        plt.plot(x,y)
        plt.title("Time Evolution of Heat Equation Solver")
        writer.grab_frame()

I took the bulk of this from the MatPlotLib MovieWriter documantation, and when I run this code, I do not understand why it takes so long.

Is there a better way to accomplish this task? Or does my code above have some bug I am unaware of? Thanks in advance.


Solution

  • You can save time by reusing the same plot object instead of creating a new one on every iteration. You have the following line in your code:

    l, = plt.plot([], [], 'k-o')
    

    Where you seem to create a plot for reusing it later, but then l is not used after. Try with something like this:

    import numpy as np
    import matplotlib
    matplotlib.use("Agg")
    import matplotlib.pyplot as plt
    import matplotlib.animation as manimation
    
    
    FFMpegWriter = manimation.writers['ffmpeg']
    metadata = dict(title='Movie Test', artist='Matplotlib',
                    comment='Movie support!')
    writer = FFMpegWriter(fps=15, metadata=metadata)
    
    fig = plt.figure()
    l, = plt.plot([], [], 'k-o')
    l.set_title("Time Evolution of Heat Equation Solver")
    
    solverlist = ["explicit", "implicit", "crank-nicolson"]
    filename = f"{solverlist[2]}-solver/cn_output_400_nodes.txt"
    loaded_matrix = np.loadtxt(filename, dtype='f', delimiter=' ')
    
    with writer.saving(fig, f"{solverlist[2]}_400_node_solution.mp4", 100):
        x = np.linspace(0.0, 2.0, len(loaded_matrix[0]))
        for i in range(len(loaded_matrix)):
            y = loaded_matrix[i]
            l.set_data(x, y)
            writer.grab_frame()
    

    In my experience, writing video with Matplotlib and ffmpeg is never very fast, but there is a significant different when you reuse objects instead of recreating them.