Search code examples
pythonmatplotlibfipy

FiPy viewer logarithmic axes scaling and aspect ratio


Is there an easy way to make different axes scale logarithmically? I am using the Matplotlib2DGridContourViewer and I managed to make the plotted data scale logarithmically using fipy.Viewer(vars=somevariable, log=True) but I couldn't find anything regarding the axes scaling. In my case I just need the y axis logarithmic.

Also I have another question about the aspect ratio of the viewer. In the documentation of Matplotlib2DGridContourViewer there is a property called figaspect:

figaspect (float, optional) – desired aspect ratio of figure. If a number, use that aspect ratio. If auto, the aspect ratio will be determined from the vars’s mesh.

I work in Jupyter Notebook and if I set a desired number as aspect ratio e.g. 0.5 it doesn't change the ratio of the lengths of the axes, but rather the aspect ratio of the whole viewer/figure area which means the data won't be more readable, just the viewer area gets squeezed with the plot aspect ratio unchanged. The reason for me wanting to change the axes length ratio is that I have a 2D mesh with 1000x1000 cells and for some reason the default aspect ratio is not determined by that (1:1), but rather from the set maximum coordinates for mesh.x and mesh.y. This way if I want to examine a 1:100 mesh I get a basically unreadable, very long plot. (I understand why it is implemented this way but I'm using the 2D mesh for plotting time dependency on a 1D mesh so the time and space coordinates are not even close.)

I guess my question is that is there any way tom make figaspect work the way I want, or is there any other relatively easy way to be able to set the ratio of axes legths? If I could tie the aspect ratio to the number of mesh cells that would also be acceptable.


Solution

  • Matplotlib2DGridContourViewer wraps matplotlib contourf() which does not appear to offer any direct option for log scaling, however, all MatplotlibViewer subclasses have an .axes property. You should be able to call viewer.axes.set_yscale() to get log scaling.

    As to figaspect, a concrete example of what you're trying to do would be helpful, but I think I understand. figaspect controls the aspect ratio of the figure. It is used to generate the figsize= argument to matplotlib figure(). If you set figaspect='auto', then FiPy tries to set the aspect ratio of the figure, including the colorbar, to respect the aspect ratio of the Mesh. I don't know what it does when you set the figaspect to something else and try to view only a subset of the Mesh.

    It does appear that Matplotlib2DGridContourViewer doesn't respect the aspect ratio of the Mesh when the aspect ratio becomes very large. Matplotlib2DGridViewer works as expected. Compare

    import fipy as fp
    mesh = fp.Grid2D(nx=100, dx=0.5, ny=100, dy=1)
    var = fp.CellVariable(mesh=mesh, name=r"$\phi$")
    cviewer = fp.Matplotlib2DGridContourViewer(vars=var)
    gviewer = fp.Matplotlib2DGridViewer(vars=var)
    

    to

    import fipy as fp
    mesh = fp.Grid2D(nx=100, dx=0.01, ny=100, dy=1)
    var = fp.CellVariable(mesh=mesh, name=r"$\phi$")
    cviewer = fp.Matplotlib2DGridContourViewer(vars=var)
    gviewer = fp.Matplotlib2DGridViewer(vars=var)
    

    I don't know why this is. I neither use Matplotlib2DGridContourViewer nor model on crazy aspect ratio meshes. I've filed a ticket so this doesn't get lost.

    Ultimately, I'm not convinced that using a 2D CellVariable to store 1D+time data is a great idea. I'd think you'd be better off extracting the 1D data into a numpy array and then using matplotlib directly to render whatever you're interested in. The whole point of a CellVariable and of a Viewer is to store and render data that lies on the geometry and topology of a Mesh. None of it's designed to deal with Mesh dimensions that aren't spatial.