-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Method to make multiple animations in one figure #38
Conversation
Hello @johnomotani! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
Comment last updated at 2019-12-10 12:37:44 UTC |
9265cee
to
e9bab98
Compare
Codecov Report
@@ Coverage Diff @@
## master #38 +/- ##
==========================================
+ Coverage 43.47% 47.61% +4.14%
==========================================
Files 8 8
Lines 759 1134 +375
Branches 144 255 +111
==========================================
+ Hits 330 540 +210
- Misses 375 535 +160
- Partials 54 59 +5
Continue to review full report at Codecov.
|
e9bab98
to
5b45807
Compare
Sorry, I made this PR too soon, should have tried out the new method more first. I've rebased on the current version of 1d-animations now, and fixed a few issues with the original code at the same time. |
Previously contained 'animate over <>', but the variable being animated over is shown on the time-slider anyway. Shortening the titles makes things clearer when multiple plots are animated at once.
5b45807
to
39e8fb9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is cool. Have you tried running it with real data? I'm a bit concerned that there might be bugs upstream in animatplot.imshow
. Also we should think about how the 2D animations will combine with #34.
xbout/plotting/animate.py
Outdated
@@ -7,7 +7,7 @@ | |||
|
|||
|
|||
def animate_imshow(data, animate_over='t', x='x', y='y', animate=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's likely that we shouldn't actually be using imshow
, because it's restricted to square images right? Though I originally wrote this for slab cross-sections, then really this should use pcolormesh
so that it can handle any geometry? Integrating it with the multi-region plotting method used in #34 will add a bit of complexity (though I think the blocks abstraction will mean it's not too hard).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think I agree with all of this, although it does need a bit of thinking... Having poloidal plots with geometry included is (very, very) nice to have as an option (maybe even default), but having cartesian as an option too is also useful - for example poloidal plotting of some arbitrary slice is going to be a bit tricky*; also if there are some very small or distorted cells they may be easier to see in a plot that's just rectangular. The blocks abstraction does very much help with all of this, we can add over time functions that create a block in various ways, and code like this that sticks blocks together would only need to add some logic to say which function to call.
It probably would be sensible to use pcolormesh
instead of imshow
everywhere though, since it's much more flexible.
--
- Maybe it would be useful to introduce some 'coordinates' that say which region a point is, e.g.
xregion
wherexregion=0
forx<ixseps1
,xregion=1
forixseps1<=x<ixseps2
,xregion=2
forx>=ixseps2
[note ixseps1/ixseps2 include the x-boundary cells, so need to subtractMXG
if x does not include the boundary cells]. Hopefully this can be done in such a way that if you do something liken.isel(x=slice(3,7,1)
thenxregion
would get sliced as well. Then we could select regions to join withxregion
and ayregion
(defined usingjyseps*
)?
I have been using it, and it works. Animations sometimes seem to freeze in a jupyter notebook, but I've not worked out how to reproduce yet. It might also be an issue with my setup, I'm currently having problems with matplotlib in ipython, where pyplot.show() is not blocking. Need to spend more time observing when I have problems before trying seriously to debug... |
animatplot's Pcolormesh seems to need x- and y-value arrays to be passed explicitly, otherwise the animations looked strange.
'norm' option allows any matplotlib.colors.Normalize instance to be passed, for general control of the color-scale. For convenience, also adds a 'logscale' option which can be set to True to create a standard log-scale if vmin and vmax are both positive or both negative, and a symmetric log-scale with a linear threshold of min(abs(vmin),abs(vmax))*1.e-5 if vmin and vmax have opposite signs. If a float is passed for 'logscale' and vmin and vmax have opposite signs then the value of 'logscale' is used instead of the default 1.e-5 to set the linear threshold.
Had been mixed up in an automatic merge.
Looks good to me. Did a few animations, no issues, worked well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a big PR! I've made some comments throughout. I think this is complicated enough that it will take multiple PRs to iron out any bugs and so on
xbout/plotting/utils.py
Outdated
@@ -41,26 +41,29 @@ def _decompose_regions(da): | |||
j11, j12, j21, j22, ix1, ix2, nin, _, ny, y_boundary_guards = _get_seps(da) | |||
regions = [] | |||
|
|||
x, y = da.dims[-2:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#68 is merged now so we can replace this with
x = da.attrs['bout_xdim']
y = da.attrs['bout_ydim']
xbout/plotting/utils.py
Outdated
ystart = 0 # Y index to start the next section | ||
if j11 >= 0: | ||
# plot lower inner leg | ||
region1 = da[:, ystart:(j11 + 1)] | ||
region1 = da.isel(**{y: slice(ystart, (j11 + 1))}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The keyword argument unpacking can be replaced
region1 = da.isel(y=slice(ystart, (j11 + 1)))
print("{} data passed has {} dimensions - making poloidal plot with " | ||
"animate_poloidal()".format(variable, str(n_dims))) | ||
if x is not None: | ||
kwargs['x'] = x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this now default to ds.attrs['bout_xdim']
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. Leaving the default as None
passes off setting defaults to the called methods, so lets animate_poloidal
use (R,Z)
and animate_pcolormesh
use da.dims
.
xbout/boutdataset.py
Outdated
try: | ||
if len(poloidal_plot) != len(variables): | ||
raise ValueError('if poloidal_plot is a sequence, it must have the same ' | ||
'number of elements as "variables"') | ||
except TypeError: | ||
poloidal_plot = [poloidal_plot] * len(variables) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will behave weirdly if you get a TypeError
that wasn't the one you were expecting. Perhaps instead we should do
if isinstance(poloidal_plot, bool):
poloidal_plot = [poloidal_plot] * len(variables)
elif len(poloidal_plot) != len(variables)
raise ValueError('if poloidal_plot is a sequence, it must have the same '
'number of elements as "variables"')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used 4 times, so could even refactor into a function:
def _expand_arg_list(sequence, arg_name, num_vars):
if isinstance(sequence, bool):
sequence = [sequence] * num_vars
elif len(sequence) != num_vars
raise ValueError(f'if {arg_name} is a sequence, it must have the same '
'number of elements as "variables"')
return sequence
poloidal_plot = _expand_arg_list(poloidal_plot, 'poloidal_plot', len(variables))
vmin = _expand_arg_list(vmin, 'vmin', len(variables))
vmax = _expand_arg_list(vmax, 'vmax', len(variables))
logscale = _expand_arg_list(logscale, 'logscale', len(variables))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as somewhere to note this: to test for a sequence, as suggested on https://stackoverflow.com/questions/2937114/python-check-if-an-object-is-a-sequence, probably best pattern is to use isinstance(variable, collections.Sequence)
.
xbout/boutdataset.py
Outdated
logscale = [logscale] * len(variables) | ||
|
||
blocks = [] | ||
for this in zip(variables, axes.flatten(), poloidal_plot, vmin, vmax, logscale): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use this
as a variable name 🙏
(subplot
?)
xbout/plotting/plotfuncs.py
Outdated
gridlines : bool or int, optional | ||
If True, draw grid lines on the plot. If an int is passed, it is used as the | ||
stride when plotting grid lines (to reduce the number on the plot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would have been good to separate these into a different PR but no matter
xbout/plotting/plotfuncs.py
Outdated
if gridlines is not None: | ||
if gridlines is True: | ||
gridlines = (1, 1) | ||
if not isinstance(gridlines, collections.Sequence): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
collections.sequence
seems an odd choice of container class? We just need to store either one or two ints right? Why not just a short list? Or even a dictionary?
gridlines= {'x': 1, 'y':1}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this was written quickly. Restricting to int
, slice
or dict of int or slice
is better.
xbout/plotting/plotfuncs.py
Outdated
Z_regions = _decompose_regions(da['Z']) | ||
|
||
for R, Z in zip(R_regions, Z_regions): | ||
plt.plot(R[::gridlines[0], :].T, Z[::gridlines[0], :].T, color='k', lw=0.1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a dictionary would eliminate the "magic numbers" 0 and 1 here
assert isinstance(animations.blocks[1], Pcolormesh) | ||
assert isinstance(animations.blocks[2], Line) | ||
|
||
def test_animate_list_fps(self, create_test_file): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't testing that the fps was actually set. Should do
assert animations.fps == fps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(also to be really nitpicky the animatplot logic is that you have created one animation (singular) which consists of multiple blocks (plural))
It is, sorry! Do you want me to try and split it up? |
That might be a good idea - there is at least one clear division: the new arguments to |
Can now use 'bout_xdim' and 'bout_ydim' metadata to find the renamed x- and y-dimensions.
Also specify in docstring what happens if a dict is passed.
Returned object is a single animatplot 'animation' with multiple blocks, not a list of things.
Made some updates to address @TomNicholas's review. Will try to split PR now. |
Plot multiple variables at once, like
The variables need to be sliced to 1D+1 or 2D+1 first (can mix 1d and 2d in one call though).