Skip to content

Commit 6d59de7

Browse files
authored
Implementation of Spaghetti plot (#80)
* 1d cut of a 2d result, `plot2d_cut` * implementation spaghetti plot, `plot_spaghetti` (exported)
1 parent 5345a0f commit 6d59de7

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "HarmonicBalance"
22
uuid = "e13b9ff6-59c3-11ec-14b1-f3d2cc6c135e"
33
authors = ["Jan Kosata <[email protected]>", "Javier del Pino <[email protected]>"]
4-
version = "0.6.2"
4+
version = "0.6.3"
55

66
[deps]
77
BijectiveHilbert = "91e7fc40-53cd-4118-bd19-d7fcd1de2a54"

src/plotting_Plots.jl

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using Plots, Latexify
2-
import Plots.plot, Plots.plot!; export plot, plot!, plot_phase_diagram, savefig
2+
import Plots.plot, Plots.plot!; export plot, plot!, plot_phase_diagram, savefig, plot_spaghetti
33

44
const _set_Plots_default = Dict{Symbol, Any}([
55
:fontfamily => "computer modern",
66
:titlefont => "computer modern",
77
:tickfont => "computer modern",
88
:linewidth => 2,
9-
:legend => :outerright])
9+
:legend_position => :outerright])
1010

1111

1212

@@ -43,11 +43,15 @@ To make the 2d plot less chaotic it is required to specify the specific `branch`
4343
4444
The x and y axes are taken automatically from `res`
4545
"""
46-
function plot(res::Result, varargs...; kwargs...)::Plots.Plot
46+
function plot(res::Result, varargs...; cut=Pair(missing, missing), kwargs...)::Plots.Plot
4747
if dim(res) == 1
4848
plot1D(res, varargs...; _set_Plots_default..., kwargs...)
4949
elseif dim(res) == 2
50-
plot2D(res, varargs...; _set_Plots_default..., kwargs...)
50+
if ismissing(cut.first)
51+
plot2D(res, varargs...; _set_Plots_default..., kwargs...)
52+
else
53+
plot2D_cut(res, varargs...; cut=cut, _set_Plots_default..., kwargs...)
54+
end
5155
else
5256
error("Data dimension ", dim(res), " not supported")
5357
end
@@ -181,8 +185,47 @@ function plot2D(res::Result; z::String, branch::Int64, class="physical", not_cla
181185
p = add ? Plots.plot!() : Plots.plot() # start a new plot if needed
182186

183187
ylab, xlab = latexify.(string.(keys(res.swept_parameters)))
184-
p = plot!(map(_realify, [Y, X, Z])...;
185-
st=:surface, color=:blue, opacity=0.5, xlabel=xlab, ylabel=ylab, zlabel=latexify(z), colorbar=false, kwargs...)
188+
p = plot!(map(_realify, [Y, X, Z])...;
189+
st=:surface, color=:blue, opacity=0.5, xlabel=xlab, ylabel=ylab, zlabel=latexify(z), colorbar=false, kwargs...)
190+
end
191+
192+
function plot2D_cut(res::Result; y::String, cut::Pair, class="default", not_class=[], add=false, kwargs...)
193+
194+
if class == "default"
195+
if not_class == [] # plot stable full, unstable dashed
196+
p = plot2D_cut(res; y=y, cut=cut, class=["physical", "stable"], add=add, kwargs...)
197+
plot2D_cut(res; y=y, cut=cut, class="physical", not_class="stable", add=true, style=:dash, kwargs...)
198+
return p
199+
else
200+
p = plot2D_cut(res; y=y, cut=cut, not_class=not_class, class="physical", add=add, kwargs...)
201+
return p
202+
end
203+
end
204+
205+
# the swept params are ranges and thus a sorted search can be performed
206+
cut_par, cut_value = cut
207+
cut_par_index = searchsortedfirst(res.swept_parameters[cut_par], cut_value)
208+
209+
# compare strings beacuse type Num cannot be compared
210+
swept_pars = res.swept_parameters.keys
211+
x_index = findfirst(sym -> string(sym)!=string(cut_par), swept_pars)
212+
isnothing(x_index) && error("The variable $cut_par was not swept over.")
213+
x = swept_pars[x_index]
214+
215+
X = res.swept_parameters[x]
216+
Y =_apply_mask(transform_solutions(res, y), _get_mask(res, class, not_class)) # first transform, then filter
217+
branches = _realify(x_index==1 ? Y[:, cut_par_index] : Y[cut_par_index, :])
218+
219+
# start a new plot if needed
220+
p = add ? Plots.plot!() : Plots.plot()
221+
222+
# colouring is matched to branch index - matched across plots
223+
for k in findall(branch -> !all(isnan.(branch)), branches[1:end]) # skip NaN branches but keep indices
224+
l = _is_labeled(p, k) ? nothing : k
225+
Plots.plot!(X, branches[k]; color=k, label=l, xlabel=latexify(string(x)), ylabel=latexify(y), kwargs...)
226+
end
227+
228+
return p
186229
end
187230

188231

@@ -219,7 +262,7 @@ end
219262
plot_phase_diagram(res::Result, class::String; kwargs...) = plot_phase_diagram(res; class=class, kwargs...)
220263

221264

222-
function plot_phase_diagram_2D(res::Result; class="physical", not_class=[], kwargs...)
265+
function plot_phase_diagram_2D(res::Result; class="physical", not_class=[], kwargs...)::Plots.Plot
223266
X, Y = values(res.swept_parameters)
224267
Z = sum.(_get_mask(res, class, not_class))
225268

@@ -230,12 +273,55 @@ function plot_phase_diagram_2D(res::Result; class="physical", not_class=[], kwar
230273
end
231274

232275

233-
function plot_phase_diagram_1D(res::Result; class="physical", not_class=[], kwargs...)
276+
function plot_phase_diagram_1D(res::Result; class="physical", not_class=[], kwargs...)::Plots.Plot
234277
X = values(res.swept_parameters)
235278
Y = sum.(_get_mask(res, class, not_class))
236279
plot(X..., Y; xlabel=latexify(string(keys(res.swept_parameters)...)), ylabel="#", legend=false, yticks=1:maximum(Y), kwargs...)
237280
end
238281

282+
###
283+
# Spaghetti Plot
284+
###
285+
286+
function plot_spaghetti(res::Result; x::String, y::String, z::String, class="default", not_class=[], add=false, kwargs...)::Plots.Plot
287+
288+
if class == "default"
289+
if not_class == [] # plot stable full, unstable dashed
290+
p = plot_spaghetti(res; x=x, y=y, z=z, class=["physical", "stable"], add=add, kwargs...)
291+
plot_spaghetti(res; x=x, y=y, z=z, class="physical", not_class="stable", add=true, style=:dash, kwargs...)
292+
return p
293+
else
294+
p = plot_spaghetti(res; x=x, y=y, z=z, class="physical", not_class=not_class, add=add, kwargs...)
295+
return p
296+
end
297+
end
298+
299+
vars = res.problem.variables
300+
x_index = findfirst(sym -> string(sym)==x, vars)
301+
y_index = findfirst(sym -> string(sym)==y, vars)
302+
isnothing(x_index) && error("The variable $x is not a defined variable.")
303+
isnothing(y_index) && error("The variable $y is not a defined variable.")
304+
305+
swept_pars = res.swept_parameters.keys
306+
z_index = findfirst(sym -> string(sym)==z, swept_pars)
307+
isnothing(z_index) && error("The variable $z was not swept over.")
308+
309+
Z = res.swept_parameters.vals[z_index]
310+
X = _apply_mask(transform_solutions(res, x), _get_mask(res, class, not_class)) |> _realify
311+
Y = _apply_mask(transform_solutions(res, y), _get_mask(res, class, not_class)) |> _realify
312+
313+
# start a new plot if needed
314+
p = add ? Plots.plot!() : Plots.plot()
315+
316+
# colouring is matched to branch index - matched across plots
317+
for k in findall(x -> !all(isnan.(x)), X[1:end]) # skip NaN branches but keep indices
318+
l = _is_labeled(p, k) ? nothing : k
319+
Plots.plot!(X[k], Y[k], Z; _set_Plots_default...,
320+
color=k, label=l, xlabel=latexify(x), ylabel=latexify(y), zlabel=latexify(z), xlim=:symmetric, ylim=:symmetric, kwargs...)
321+
end
322+
return p
323+
end
324+
239325
###
240326
# TRANSFORMATIONS TO THE LAB frame
241327
###

0 commit comments

Comments
 (0)