Skip to content

Commit 839db3e

Browse files
authored
Merge pull request #12 from NonlinearOscillations/plot_outputs
plots return dictionaries
2 parents 269cced + 4373542 commit 839db3e

File tree

2 files changed

+53
-54
lines changed

2 files changed

+53
-54
lines changed

src/plotting_interactive.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function plot_2D_phase_diagram_interactive(res::Result; observable::String="nsol
203203

204204
length(vec(ax)) <= nrows*ncols || error("insufficient # of panels requested, please increase nrows or ncols") #sanity check before any plot is made
205205

206-
im,Nmax = plot_2D_phase_diagram(res; stable=stable,observable=observable,ax=ax[1])
206+
_,im,Nmax = plot_2D_phase_diagram(res; stable=stable,observable=observable,ax=ax[1]) #skip saved data dictionary
207207

208208
"Update annotations when cursor moves"
209209
function update_annot(ind)

src/plotting_static.jl

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -160,26 +160,24 @@ end
160160
"""
161161
plot_1D_solutions(res::Result;
162162
x::String, y::String,
163-
marker="o",xscale="linear",yscale="linear"
163+
xscale="linear",yscale="linear"
164164
,plot_only=["physical"],
165165
marker_classification="stable",filename=nothing, kwargs...)
166166
167167
Make a 1D plot of a `Result` object.
168168
169169
Keyword arguments
170170
- `x`, `y`: Expressions to plot on as independent/dependent variables (parsed into Symbolics.jl).
171-
- `marker`: The point marker to use.
172171
- `xscale`, `yscale`: scale for x/y dimensions (e.g. "linear" or "log")
173172
- `plot_only`: a list of strings corresponding to the solution classes of `Result`. Only solutions which belong to the listed classes are plotted.
174173
- `marker_classification`: A class of the solutions (created by `classify_solutions!`) which is distinguished with different markers. Entering an inequality creates a new class "custom_class".
175-
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`.
176-
- `col_length`: number of elements per legend column. By default 10.
174+
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`. Otherwise, data is returned as a dictionary.
177175
- `kwargs`: any additional keywords arguments for the matplotlib plotting
178176
179177
The strings in `marker_classification` allows the user to stablish custom criteria for binary classification of solutions. For instance, if `marker_classification = "ω^15* sqrt(u1^2 + v1^2) < 0.1"`,
180178
for a system with harmonic variables u1,v1, then solutions are classified as `true` according to that criterion and `false` according to its complement.
181179
"""
182-
function plot_1D_solutions(res::Result; x::String, y::String, xscale="linear",yscale="linear",plot_only=["physical"],marker_classification="stable",filename=nothing,col_length=10, kwargs...)
180+
function plot_1D_solutions(res::Result; x::String, y::String, xscale="linear",yscale="linear",plot_only=["physical"],marker_classification="stable",filename=nothing,kwargs...)
183181
_set_plotting_settings()
184182
length(size(res.solutions)) != 1 && error("1D plots of not-1D datasets are usually a bad idea.")
185183

@@ -209,13 +207,10 @@ function plot_1D_solutions(res::Result; x::String, y::String, xscale="linear",ys
209207
ax.set_prop_cycle(nothing) #reset color cycler state (NaN solutions aren't shown but the color cycler runs)
210208
append!(lines,ax.plot(X, Yu, "x"; kwargs...))
211209

212-
213-
if !isnothing(filename)
214-
xdata,ydata = [line.get_xdata() for line in lines], [line.get_ydata() for line in lines]
215-
markers = [line.get_marker() for line in lines]
216-
marker_dict = Dict(marker=>sol_type,"x"=>not_sol_type)
217-
JLD2.save(_jld2_name(filename), Dict(string(x) => xdata,string(y)=>ydata,"marker_dict"=>marker_dict,"markers"=>markers))
218-
end
210+
xdata,ydata = [line.get_xdata() for line in lines], [line.get_ydata() for line in lines]
211+
markers = [line.get_marker() for line in lines]
212+
marker_dict = Dict("o"=>sol_type,"x"=>not_sol_type)
213+
save_dict= Dict(string(x) => xdata,string(y)=>ydata,"marker_dict"=>marker_dict,"markers"=>markers)
219214

220215
ax.set_xlabel(latexify(x),fontsize=24)
221216
ax.set_ylabel(latexify(_prettify_label(res,y)),fontsize=24)
@@ -233,12 +228,14 @@ function plot_1D_solutions(res::Result; x::String, y::String, xscale="linear",ys
233228

234229
leg = cat(leg_classes, leg_branches..., dims=1)
235230

236-
ax.legend(handles=leg, bbox_to_anchor=(-0.25, 0.95))
231+
ax.legend(handles=leg, bbox_to_anchor=(-0.25, 0.95))
237232
ax.set_xscale(xscale)
238233
ax.set_yscale(yscale)
239234
f.tight_layout()
240235

241-
return X, Ys
236+
!isnothing(filename) ? JLD2.save(_jld2_name(filename), save_dict) : nothing
237+
return save_dict
238+
242239
end
243240

244241
"""
@@ -250,14 +247,14 @@ Produces a "spaghetti plot" of 1D `Result`object`, with harmonic variables in a
250247
251248
Keyword arguments
252249
- `z`: Parameter expression to plot on as dependent variables z (parsed into Symbolics.jl).
253-
- `x_scale`, `y_scale`: Factors to multiply the shown axis ticks with.
254250
- `zscale`: scale for z dimension (e.g. "linear" or "log")
255-
- `zaspect`: aspect ratio for the parameter dimension
251+
- `z_aspect`: aspect ratio for the parameter dimension
252+
- `kwargs`: any additional keywords arguments for the matplotlib plotting
256253
"""
257254

258-
function plot_1D_solutions_spaghetti(res,z::String,zscale="linear",zaspect=2)
255+
function plot_1D_solutions_spaghetti(res,z::String,z_scale="linear",zaspect=2,kwargs...)
259256
var_names = [string(v) for v in res.problem.variables]
260-
n_dof = length(var_names)÷2
257+
n_dof = length(var_names)÷2 #number of harmonic variable pairs
261258
nsolsmax = sum(any.(classify_branch(res, "physical"))) #maximum number of physical solutions
262259

263260
fig = plt.figure(figsize=(5*(2*n_dof),5),tight_layout=true)
@@ -286,14 +283,14 @@ function plot_1D_solutions_spaghetti(res,z::String,zscale="linear",zaspect=2)
286283

287284
ax = fig[:add_subplot](1,n_dof,i+1, projection="3d")
288285
for i in 1:nsolsmax
289-
ax.plot(real.(uS[i,:]),real.(vS[i,:]),zs[i,:],lw=3)
290-
ax.plot(real.(uU[i,:]),real.(vU[i,:]),zs[i,:],ls="--",lw=3)
286+
ax.plot(real.(uS[i,:]),real.(vS[i,:]),zs[i,:]; kwargs...)
287+
ax.plot(real.(uU[i,:]),real.(vU[i,:]),zs[i,:],ls="--";kwargs...)
291288
end
292289
ax.set_xlabel( HarmonicBalance.latexify(_prettify_label(res,var_names[2*i+1])),fontsize=24,labelpad=15)
293290
ax.set_ylabel( HarmonicBalance.latexify(_prettify_label(res,var_names[2*i+2])),fontsize=24,labelpad=15)
294291
ax.set_zlabel( HarmonicBalance.latexify(z),fontsize=24,labelpad=15)
295292

296-
ax.set_zscale(zscale)
293+
ax.set_zscale(z_scale)
297294
ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
298295
ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
299296
ax.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
@@ -323,7 +320,7 @@ end
323320

324321

325322
"""
326-
plot_1D_jacobian_eigenvalues(res::Result; x::String, physical=true, stable=false,marker_re="o",marker_im="X", filename=nothing)
323+
plot_1D_jacobian_eigenvalues(res::Result; x::String, physical=true, stable=false,marker_re="o",marker_im="X", filename=nothing,kwargs...)
327324
328325
Make a 1D plot of the Jacobian eigenvalues for each of the solutions in a `Result` object.
329326
@@ -333,10 +330,10 @@ Keyword arguments
333330
- `physical`, `stable`: Booleans specifying whether unphysical and/or unstable solutions are shown.
334331
- `marker_re`, `marker_im`: The markers to use for the Re and Im parts of the eigenvalues.
335332
- `ax`: axis object from `PyCall.PyObject` setting the coordinate system where data will be plotted. If not given, it is created automatically.
336-
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`.
333+
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`. Otherwise, data is returned as a dictionary.
337334
338335
"""
339-
function plot_1D_jacobian_eigenvalues(res::Result; x::String, physical=true, stable=false,marker_re="o",marker_im="X",ax=nothing, filename=nothing)
336+
function plot_1D_jacobian_eigenvalues(res::Result; x::String, physical=true, stable=false,marker_re="o",marker_im="X",ax=nothing, filename=nothing, kwargs...)
340337
_set_plotting_settings()
341338

342339
xplot = transform_solutions(res, x) #independent variable to plot
@@ -372,30 +369,32 @@ function plot_1D_jacobian_eigenvalues(res::Result; x::String, physical=true, sta
372369
end
373370

374371
X_plot = real.([xplot[idx][branch] for idx in 1:length(res.solutions)])
375-
append!(lines_re,ax[branch].plot(X_plot,real.(λs),string(marker_re,"r")))
376-
append!(lines_im,ax[branch].plot(X_plot,imag.(λs),string(marker_im,"g"))) #
372+
append!(lines_re,ax[branch].plot(X_plot, real.(λs), string(marker_re,"r"); kwargs...))
373+
append!(lines_im,ax[branch].plot(X_plot, imag.(λs), string(marker_im,"g"); kwargs...)) #
377374
ax[branch].set_title(string("solution ", branch),fontsize=12,loc="left");
378375
ax[branch].set_xlabel(latexify(string(x)),fontsize=24)
379376

380377
end
381378

379+
save_dict = Dict(zip(["re","im"],[Dict("data"=>Dict(),"marker"=>[]) for d in ["re","im"]]))
380+
for (axis,lines,marker) in zip(["re","im"],[lines_re,lines_im],[marker_re,marker_im])
381+
xdata = [line.get_xdata() for line in lines]
382+
ydata = [line.get_ydata() for line in lines]
383+
save_dict[axis]["data"] = Dict(string(x) => xdata,string(axis," part(eig)")=>ydata)
384+
save_dict[axis]["marker"] = marker
385+
end
386+
382387
if !isnothing(filename)
383-
save_dict = Dict(zip(["re","im"],[Dict("data"=>Dict(),"marker"=>[]) for d in ["re","im"]]))
384-
for (axis,lines,marker) in zip(["re","im"],[lines_re,lines_im],[marker_re,marker_im])
385-
xdata = [line.get_xdata() for line in lines]
386-
ydata = [line.get_ydata() for line in lines]
387-
save_dict[axis]["data"] = Dict(string(x) => xdata,string(axis," part(eig)")=>ydata)
388-
save_dict[axis]["marker"] = marker
389-
end
390-
391388
JLD2.save(_jld2_name(filename), save_dict)
389+
else
390+
return save_dict
392391
end
393392

394393
legend_elements = [plt.Line2D([0], [0], marker=marker_re, color="w", label=L"\Re({\mathrm{eig}(J)})",
395394
markerfacecolor="r", markersize=7),
396395
plt.Line2D([0], [0], marker=marker_im, color="w", label=L"\Im({\mathrm{eig}(J)})",
397396
markerfacecolor="g", markersize=7)]
398-
ax[1].legend(handles=legend_elements,loc="best",fontsize=15)
397+
ax[1].legend(handles=legend_elements;)
399398
end
400399

401400
"""Take a set of conditions and multi-solution maps on solutions transformed by `z`
@@ -441,7 +440,7 @@ Make a 2D plot of each of solutions vs swept parameters for a `Result` object, o
441440
Keyword arguments
442441
443442
- `ax`: axis object from `PyCall.PyObject` setting the coordinate system where data will be plotted. If not given, it is created automatically.
444-
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`.
443+
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`. Otherwise, data is returned as a dictionary.
445444
- `z`: The function on the z axis (a string parsed into Symbolics.jl). If `z=nothing`, raw solutions are displayed
446445
- `plot_only`: Array of labels to filter physical solutions (e.g. "stable") and multi-solution methods if `z!=nothing` (e.g. maximum)
447446
"""
@@ -485,10 +484,10 @@ function plot_2D_solutions(res::Result; ax=nothing, filename=nothing, z=nothing,
485484
for l in 1:nrow
486485
a = ax[l,m].imshow(Z[m,l,:,end:-1:1]',extent=extent,aspect="auto")
487486
colorbar(a,ax=ax[l,m])
488-
if !isnothing(filename)
489-
save_dict[string("panel (",m,",",l,")")]= Dict("variable"=>var_names[m],"solution #"=>l,"data"=>a.get_array(),
490-
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent)
491-
end
487+
488+
save_dict[string("panel (",m,",",l,")")]= Dict("variable"=>var_names[m],"solution #"=>l,"data"=>a.get_array(),
489+
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent)
490+
492491
end
493492
ax[1,m].set_title(latexify(_prettify_label(res,var_names[m])),fontsize=20)
494493
end
@@ -503,10 +502,10 @@ function plot_2D_solutions(res::Result; ax=nothing, filename=nothing, z=nothing,
503502
ax[l].set_title(string("solution ",l),fontsize=18)
504503
end
505504
colorbar(a,ax=ax[l])
506-
if !isnothing(filename)
507-
save_dict[string("panel (",l,")")]= Dict("variable"=>z,"solution #"=>l,"data"=>a.get_array(),
508-
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent)
509-
end
505+
506+
save_dict[string("panel (",l,")")]= Dict("variable"=>z,"solution #"=>l,"data"=>a.get_array(),
507+
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent)
508+
510509
end
511510
f.suptitle(latexify(_prettify_label(res,z)),fontsize=18,y=1.01)
512511

@@ -518,9 +517,8 @@ function plot_2D_solutions(res::Result; ax=nothing, filename=nothing, z=nothing,
518517

519518
f.tight_layout()
520519

521-
if !isnothing(filename)
522-
JLD2.save(_jld2_name(filename), save_dict)
523-
end
520+
!isnothing(filename) ? JLD2.save(_jld2_name(filename), save_dict) : nothing
521+
return save_dict
524522
end
525523

526524
"""Discrete colorbar preparation for phase diagram"""
@@ -542,7 +540,7 @@ Keyword arguments
542540
- `stable`: whether only stable solutions are depicted
543541
- `observable`: reference observable to represent dynamical phases in the problem. If `observable="nsols"`, number of solutions for each point is shown.
544542
If instead `observable="binary"`, the result of classification of bistrings `[is_stable(solution_1),is_stable(solution_2),...]` is presented (see `classify_binaries!(Result)` function).
545-
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`.
543+
- `filename`: if different from `nothing`, plotted data and parameter values are exported to `./filename.jld2`.Otherwise, data is returned as a dictionary.
546544
547545
"""
548546
function plot_2D_phase_diagram(res::Result; stable=false,observable="nsols",ax=nothing, filename=nothing)
@@ -584,9 +582,10 @@ function plot_2D_phase_diagram(res::Result; stable=false,observable="nsols",ax=n
584582
ax.set_ylabel(latexify(py),fontsize=24)
585583
ax.set_title(pd_title)
586584

587-
if !isnothing(filename)
588-
JLD2.save(_jld2_name(filename), Dict("observable"=>observable,"data"=>im.get_array(),
589-
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent))
590-
end
591-
im,Nmax
585+
save_dict = Dict("observable"=>observable,"data"=>im.get_array(),
586+
string("(",px,"_min ",px,"_max ",py,"_min ",py,"_max)")=>extent)
587+
588+
!isnothing(filename) ? JLD2.save(_jld2_name(filename),save_dict) : nothing
589+
return save_dict,im,Nmax
590+
592591
end

0 commit comments

Comments
 (0)