setting fixed color bar values

1.5k views Asked by At

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

2

There are 2 answers

0
Jody Klymak On BEST ANSWER

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))

2
arty On

@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)