2

I'm plotting some nodal points data animated in time steps as follows:

fig, ax = plt.subplots()
fig.tight_layout()
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

def animate(i):
    ax.cla()
    plt.cla()
    ax.set_aspect('equal', 'box')
    c = ax.tricontourf(triang, z[:, i], 10, cmap='plasma')
    c.set_clim(np.min(z), np.max(z))
    plt.colorbar(c, cax=cax)

anim = FuncAnimation(fig, animate, interval=100, frames=nt)

Where z - is nnodes x number_of_timesteps matrix of nodal values. But as you can see on the picture below, the colorbar range and values does not seem to be fixed. I mean the values assigned to a particular color seems to be fixed, but the color legend is changing in time. I thought c.set_clim(np.min(z), np.max(z)) should fix it, as it takes minimum and maximum nodal values from the whole set of data at every time step, but apparently it does not fix the colorbar. Is there a way to work it out?

enter image description here enter image description here enter image description here

arty
  • 75
  • 8
  • Probably this would work in your case: `ax.tricontourf(..., vmin=np.min(z), vmax=np.max(z))`, leaving out the call to set_clim. Note that the color is assigned inside `tricontourf`. The colorbar is just a helpful tool to show how the colors were assigned. – JohanC Feb 09 '20 at 23:11
  • @JohanC, Yes, I didn't mention it, but I've tried it exactly this way. The result is the same... So it looks like when the color disappears from the plot, colorbar got rescaled and the color disappears from it as well. But I thought there is a way to make it static... – arty Feb 09 '20 at 23:18
  • If z and thus np.min(z) and np.max(z) and thus vmin and vmax stay constant, should the corresponding colorbar also stay constant? Maybe np.min(z) and max could be printed out inside the animate function to see what's happening? – JohanC Feb 09 '20 at 23:38
  • @JohanC, Checked them, they indeed remain constant every iteration inside the animate function, what is expected. – arty Feb 09 '20 at 23:53
  • @arty: Can you post full code with data? I can give a try – SKPS Feb 10 '20 at 00:35
  • Set vmin=-2e6 vmax=2e6. If those are i no it correct then set to other values. – Jody Klymak Feb 10 '20 at 02:43
  • @Jody Klymak, tried, didn't work – arty Feb 10 '20 at 10:25
  • Well then you have found a bug. Please kindly open an issue on github with a minimal reproducible example. – Jody Klymak Feb 10 '20 at 15:14

2 Answers2

2

You are getting a different colorbar each time because you are not specifying your levels of your contour. Try:

c = ax.tricontourf(triang, z[:, i], 10, cmap='plasma', vmin=-1, vmax=1, levels=np.arange(-1, 1.02, 0.1))

Jody Klymak
  • 4,979
  • 2
  • 15
  • 31
0

@SKPS, here we go. In the script below I am using fake function of coordinates, just to give you some values, as original one is based on FEM routine and therefore it is quite massive. With the provided link you can download the mesh file used in the script. In the code below I have tried it two ways: FuncAnimation, which is used in the animate_plot() and ArtistAnimation, which is in the animate_plot2(). You can switch between them by editing the last line of the code. See, the issues with FuncAnimation been already described. When using ArtistAnimation, the colorbar seems to be static, but in fact it is the colorbar of the very last time step. In order to demonstrate it I have changed the plotted function in animate_plot2(), so if you run it, you will see what I mean. Also in this case you see that the title of the plot is not updated any more - it always shows one value. I tried to fix it by adding lines ax.cla() and plt.cla() as in previous case, but it screws it up even more.

Kind regards.

the mesh: https://www.dropbox.com/s/x4njq0t93636wfv/new_cave.msh?dl=0

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.tri as mtri
import meshio

from matplotlib.animation import FuncAnimation
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.offsetbox import (AnchoredOffsetbox, DrawingArea, HPacker,
                                  TextArea)


def load_mesh(mesh_filename):
    m = meshio.read(mesh_filename)
    p = m.points.transpose()
    p = np.delete(p, 2, axis=0)
    t = m.cells["triangle"]
    return p, t


def animate_plot(nt, p, t):
    x = p[0, :]
    y = p[1, :]
    triang = mtri.Triangulation(x, y, t)
    nnodes = len(p[0])
    z = np.zeros((nnodes, nt))
    for j in range(nt):
        z[:, j] = j ** 2 * (np.sin(x * 10) + np.sin(y * 10))

    fig, ax = plt.subplots()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)

    def animate(i):
        ax.cla()
        plt.cla()
        ax.set_aspect('equal', 'box')
        ax.set(xlim=(min(x), max(x)), ylim=(min(y), max(y)))
        c = ax.tricontourf(triang, z[:, i], 10, cmap='plasma', vmin=-1, vmax=1)
        c.set_clim(np.min(z), np.max(z))
        ax.triplot(triang, color='white', lw=0.1)
        ax.set_title('test, ' + 'np.min(z)=' + str(np.min(z)) + ', np.max(z)=' + str(np.max(z)) + '.')
        cbar = plt.colorbar(c, cax=cax, format='%.0e')
        ax.set_xlabel('x [m]')
        ax.set_ylabel('y [m]')

    anim = FuncAnimation(
        fig, animate, interval=600, frames=nt)
    anim.save('test.gif', writer='imagemagick')


def animate_plot2(nt, p, t):
    x = p[0, :]
    y = p[1, :]
    triang = mtri.Triangulation(x, y, t)
    nnodes = len(p[0])
    z = np.zeros((nnodes, nt))
    for j in range(nt):
        z[:, j] = 100 * (np.sin(x * 10) + np.sin(y * 10)) - j ** 2 * (np.sin(x * 10) + np.sin(y * 10))

    img = []
    fig, ax = plt.subplots()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)

    for i in range(nt):
        # ax.cla()
        # plt.cla()
        ax.set_aspect('equal', 'box')
        ax.set(xlim=(min(x), max(x)), ylim=(min(y), max(y)))
        fig.tight_layout()
        ax.set_title('time step = ' + str(i))
        c = ax.tricontourf(triang, z[:, i], 10, cmap='plasma')
        c.set_clim(np.min(z), np.max(z))
        ax.triplot(triang, color='white', lw=0.1)
        plt.colorbar(c, cax=cax)
        img.append(c.collections)

    name = 'test.gif'
    anim_img = animation.ArtistAnimation(fig, img, interval=300, blit=True)
    anim_img.save(name, writer='imagemagick', bitrate=300)


mesh_filename = 'new_cave.msh'
p, t = load_mesh(mesh_filename)

nt = 10
animate_plot2(nt, p, t)
arty
  • 75
  • 8
  • So `vmin, vmax` and same with `set_clim()`, they only assign color to a certain value, you can change them and see what happens. Whereas I wish to have 'static' colorbar legend for every frame - even when it is first frame when all values are zero, I want to have colorbar which would show me the whole range of colors and values from `np.min(z)` till `np.max(z)`. – arty Feb 10 '20 at 12:49
  • This should have been posted as an edit to the original question **not** as an answer. When someone prompts you to 'post the full code' they mean by editing the question - just for future reference – William Miller Feb 11 '20 at 06:27