1+ export plot_1D_solutions_branch
2+
3+
4+ """ Calculate distance between a given state and a stable branch"""
5+ function _closest_branch_index (res:: Result ,state:: Vector{Float64} ,index:: Int64 )
6+ # search only among stable solutions
7+ physical = filter_solutions .(
8+ filter_solutions .(
9+ res. solutions, res. classes[" physical" ]),res. classes[" stable" ])
10+
11+ steadystates = reduce (hcat,physical[index]) # change structure for easier indexing
12+ distances = vec (sum (abs2 .(steadystates .- state),dims= 1 ))
13+ return argmin (replace! (distances, NaN => Inf ))
14+ end
15+
16+
17+ """ Return the indexes and values following stable branches along a 1D sweep.
18+ When a no stable solutions are found (e.g. in a bifurcation), the next stable solution is calculated by time evolving the previous solution (quench).
19+
20+ Keyword arguments
21+ - `y`: Dependent variable expression (parsed into Symbolics.jl) to evaluate the followed solution branches on .
22+ - `sweep`: Direction for the sweeping of solutions. A `right` (`left`) sweep proceeds from the first (last) solution, ordered as the sweeping parameter.
23+ - `tf`: time to reach steady
24+ - `ϵ`: small random perturbation applied to quenched solution, in a bifurcation in order to favour convergence in cases where multiple solutions are identically accessible (e.g. symmetry breaking into two equal amplitude states)
25+ """
26+ function follow_branch (starting_branch:: Int64 ,res:: Result ; y= " sqrt(u1^2+v1^2)" ,sweep= " right" ,tf= 10000 ,ϵ= 1E-4 )
27+ sweep_directions = [" left" , " right" ]
28+ sweep ∈ sweep_directions || error (" Only the following (1D) sweeping directions are allowed: " , sweep_directions)
29+
30+ # filter stable solutions
31+ Y = transform_solutions (res, y)
32+ Ys = filter_solutions .(filter_solutions .(Y, res. classes[" physical" ]),res. classes[" stable" ]);
33+ Ys = real .(reduce (hcat,Ys)) # facilitate indexing. Ys is an array (length(sweep))x(#solutions)
34+
35+ followed_branch = zeros (Int64,length (Y)) # followed branch indexes
36+ followed_branch[1 ] = starting_branch
37+
38+ if sweep == " left"
39+ Ys = reverse (Ys,dims= 2 )
40+ end
41+
42+ p1 = collect (keys (res. swept_parameters))[1 ] # parameter values
43+
44+ for i in 2 : size (Ys)[2 ]
45+ s = Ys[followed_branch[i- 1 ],i] # solution amplitude in the current branch and current parameter index
46+ if ! isnan (s) # the solution is not unstable or unphysical
47+ followed_branch[i] = followed_branch[i- 1 ]
48+ else # bifurcation found
49+ if sweep == " right"
50+ next_index = i
51+ elseif sweep == " left" # reverse order of iterators
52+ next_index = length (Y)- i+ 1
53+ end
54+
55+ # create a synthetic starting point out of an unphysical solution: quench and time evolve
56+ # the actual solution is complex there, i.e. non physical. Take real part for the quench.
57+ sol_dict = get_single_solution (res, branch= followed_branch[i- 1 ], index= next_index)
58+ print (" bifurcation @ " , p1 ," = " , real (sol_dict[p1])," " )
59+ sol_dict = Dict (zip (keys (sol_dict),real .(values (sol_dict)) .+ 0.0im .+ ϵ* rand (length (values (sol_dict)))))
60+ problem_t = TimeEvolution. ODEProblem (res. problem. eom, steady_solution= sol_dict, timespan= (0 ,tf))
61+ res_t = TimeEvolution. solve (problem_t,saveat= tf)
62+
63+ followed_branch[i] = _closest_branch_index (res,res_t. u[end ],next_index) # closest branch to final state
64+
65+ print (" switched branch " , followed_branch[i- 1 ] ," -> " , followed_branch[i]," \n " )
66+ end
67+ end
68+
69+ if sweep == " left"
70+ Ys = reverse (Ys,dims= 2 )
71+ followed_branch = reverse (followed_branch)
72+ end
73+
74+ return followed_branch,Ys
75+ end
76+
77+
78+ """ 1D plot with the followed branch highlighted"""
79+ function plot_1D_solutions_branch (starting_branch:: Int64 ,res:: Result ; x:: String , y:: String , sweep= " right" ,tf= 10000 ,ϵ= 1E-4 , xscale= " linear" ,yscale= " linear" ,plot_only= [" physical" ],marker_classification= " stable" ,filename= nothing ,kwargs... )
80+ plot_1D_solutions (res; x= x, y= y, xscale= xscale,yscale= yscale,
81+ plot_only= plot_only,marker_classification= marker_classification,filename= filename,kwargs... )
82+
83+ followed_branch,Ys = follow_branch (starting_branch,res,y= y,sweep= sweep,tf= tf,ϵ= ϵ)
84+ if sweep == " left"
85+ m = " <"
86+ elseif sweep == " right"
87+ m = " >"
88+ end
89+
90+ Y_followed = [Ys[branch,param_idx] for (param_idx,branch) in enumerate (followed_branch)]
91+
92+ lines = plot (transform_solutions (res, x),Y_followed,c= " k" ,marker= m; kwargs... )
93+
94+ # extract plotted data and return it
95+ xdata,ydata = [line. get_xdata () for line in lines], [line. get_ydata () for line in lines]
96+ markers = [line. get_marker () for line in lines]
97+ save_dict= Dict (string (x) => xdata,string (y)=> ydata," markers" => markers)
98+
99+ ! isnothing (filename) ? JLD2. save (_jld2_name (filename), save_dict) : nothing
100+ return save_dict
101+ end
0 commit comments