visclaw icon indicating copy to clipboard operation
visclaw copied to clipboard

`make plots` is slow

Open giboul opened this issue 4 months ago • 0 comments

Hi! I' new here but I'd like to make a suggestion.

The make plots command can be frustratingly long when debugging. A faster solution would be to use Iplotclaw but I did not appreciate having to use the terminal at the same time (and having to minimize windows because I have a small screen).

So I wrote a small script making use of the plotframe(..., refresh=True) function and matplotlib's mpl_connect to change frames with just the arrow keys. I ended up adding numerical input (press 49 then press enter to show frame 49) and a FuncAnimation when passing the FPS=<some_frames_per_second_value> argument to make iplot.

Could this be a good addition to visclaw? Here my code

from pathlib import Path
from argparse import ArgumentParser
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from clawpack.visclaw.Iplotclaw import Iplotclaw
from clawpack.visclaw.frametools import plotframe


def interactive_plot(fps=0, **kwargs):
    """Take advantage of Iplotclaw to make an interactive plotting session."""
    ip = Iplotclaw(**kwargs)
    counter = dict(current=0, max=0, right=1, left=-1, up=1, down=-1)
    counter["max"] = len(list(Path(ip.plotdata.outdir).glob("fort.q*")))

    def update(i):
        plotframe(i, ip.plotdata, simple=ip.simple, refresh=True)
    update(0)

    def arrow_key(event):
        """If pressed key is in counter, increment index and update plot."""
        if event.key in counter:
            counter["current"]=(counter["current"]+counter[event.key])%counter["max"]
            update(counter["current"])

    frame = dict(n="")
    def to_frame(event):
        if event.key.isnumeric():
            frame["n"] += event.key
        elif event.key == "enter":
            counter["current"] = int(frame["n"] or 0)
            frame["n"] = ""
            update(counter["current"])


    if fps:
        anim = FuncAnimation(plt.gcf(), update, frames=counter["max"], interval=1e3/fps)
        return anim
    else:
        for fn in plt.get_fignums():
            plt.figure(fn).canvas.mpl_connect('key_press_event', arrow_key)
            plt.figure(fn).canvas.mpl_connect('key_press_event', to_frame)


if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("fps", default=0, type=float)
    args = parser.parse_args()
    anim = interactive_plot(**args.__dict__)
    plt.show(block=True)

And this is what I added to the Makefile.common in clawutil to have it avaliable from any folder:

FPS = 0
iplot:
	$(CLAW_PYTHON) $(CLAW)/visclaw/src/python/visclaw/arrowkeyplot.py $(FPS)

So for example, here are the possible uses of the above code:

  • Run make iplot then...
    • Enter some frame number (say 10) then press enter (carriage return) to show the 11th frame (frame index 10).
    • then use the left/right or up/down arrow keys to navigate through the frames.
  • Run make iplot FPS=10 and watch a looping animation.

The plt.show() command is outside the function so that one is free to import the interactive_plot function and either show it later or save the FuncAnimation.

Edit: I forgot to mention the main advantage of this script: you don't have to wait for the figures to be saved in .png, .html and .tex! I find that step painful when debugging.

giboul avatar Sep 27 '24 16:09 giboul