1
1
using BifurcationKit, ModelingToolkit, Test
2
2
3
- # Checks pitchfork diagram and that there are the correct number of branches (a main one and two children)
4
- let
3
+ # Simple pitchfork diagram, compares solution to native BifurcationKit, checks they are identical.
4
+ # Checks using `jac=false` option.
5
+ let
6
+ # Creaets model.
5
7
@variables t x (t) y (t)
6
8
@parameters μ α
7
9
eqs = [0 ~ μ * x - x^ 3 + α * y,
8
10
0 ~ - y]
9
11
@named nsys = NonlinearSystem (eqs, [x, y], [μ, α])
10
12
13
+ # Creates BifurcationProblem
11
14
bif_par = μ
12
15
p_start = [μ => - 1.0 , α => 1.0 ]
13
16
u0_guess = [x => 1.0 , y => 1.0 ]
14
- plot_var = x
15
-
16
- using BifurcationKit
17
- bprob = BifurcationProblem (nsys,
18
- u0_guess,
19
- p_start,
20
- bif_par;
21
- plot_var = plot_var,
22
- jac = false )
17
+ plot_var = x;
18
+ bprob = BifurcationProblem (nsys, u0_guess, p_start, bif_par; plot_var= plot_var, jac= false )
23
19
20
+ # Conputes bifurcation diagram.
24
21
p_span = (- 4.0 , 6.0 )
25
22
opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 20 )
23
+ opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
24
+ max_steps = 100 , nev = 2 , newton_options = opt_newton,
25
+ p_min = p_span[1 ], p_max = p_span[2 ],
26
+ detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 , dsmin_bisection = 1e-9 )
27
+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside= true )
28
+
29
+ # Computes bifurcation diagram using BifurcationKit directly (without going through MTK).
30
+ function f_BK (u, p)
31
+ x, y = u
32
+ μ, α = p
33
+ return [μ* x - x^ 3 + α* y, - y]
34
+ end
35
+ bprob_BK = BifurcationProblem (f_BK, [1.0 , 1.0 ], [- 1.0 , 1.0 ], (@lens _[1 ]); record_from_solution = (x, p) -> x[1 ])
36
+ bif_dia_BK = bifurcationdiagram (bprob_BK, PALC (), 2 , (args... ) -> opts_br; bothside= true )
37
+
38
+ # Compares results.
39
+ @test getfield .(bif_dia. γ. branch, :x ) ≈ getfield .(bif_dia_BK. γ. branch, :x )
40
+ @test getfield .(bif_dia. γ. branch, :param ) ≈ getfield .(bif_dia_BK. γ. branch, :param )
41
+ @test bif_dia. γ. specialpoint[1 ]. x == bif_dia_BK. γ. specialpoint[1 ]. x
42
+ @test bif_dia. γ. specialpoint[1 ]. param == bif_dia_BK. γ. specialpoint[1 ]. param
43
+ @test bif_dia. γ. specialpoint[1 ]. type == bif_dia_BK. γ. specialpoint[1 ]. type
44
+ end
45
+
46
+ # Lotka–Volterra model, checks exact position of bifurcation variable and bifurcation points.
47
+ # Checks using ODESystem input.
48
+ let
49
+ # Ceates a Lotka–Volterra model.
50
+ @parameters α a b
51
+ @variables t x (t) y (t) z (t)
52
+ D = Differential (t)
53
+ eqs = [D (x) ~ - x + a* y + x^ 2 * y,
54
+ D (y) ~ b - a* y - x^ 2 * y]
55
+ @named sys = ODESystem (eqs)
56
+
57
+ # Creates BifurcationProblem
58
+ bprob = BifurcationProblem (sys, [x => 1.5 , y => 1.0 ], [a => 0.1 , b => 0.5 ], b; plot_var = x)
59
+
60
+ # Computes bifurcation diagram.
61
+ p_span = (0.0 , 2.0 )
62
+ opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 2000 )
26
63
opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
27
64
max_steps = 100 , nev = 2 , newton_options = opt_newton,
28
65
p_min = p_span[1 ], p_max = p_span[2 ],
29
66
detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 ,
30
67
dsmin_bisection = 1e-9 )
68
+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside = true )
31
69
32
- bf = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside = true )
70
+ # Tests that the diagram has the correct values (x = b)
71
+ all ([b. x ≈ b. param for b in bif_dia. γ. branch])
33
72
34
- @test length (bf. child) == 2
73
+ # Tests that we get two Hopf bifurcations at the correct positions.
74
+ hopf_points = sort (getfield .(filter (sp -> sp. type == :hopf , bif_dia. γ. specialpoint), :x ); by= x-> x[1 ])
75
+ @test length (hopf_points) == 2
76
+ @test hopf_points[1 ] ≈ [0.41998733080424205 , 1.5195495712453098 ]
77
+ @test hopf_points[2 ] ≈ [0.7899715592573977 , 1.0910379583813192 ]
35
78
end
79
+
80
+ # Simple fold bifurcation model, checks exact position of bifurcation variable and bifurcation points.
81
+ # Checks that default parameter values are accounted for.
82
+ # Checks that observables (that depend on other observables, as in this case) are accounted for.
83
+ let
84
+ # Creates model, and uses `structural_simplify` to generate observables.
85
+ @parameters μ p= 2
86
+ @variables t x (t) y (t) z (t)
87
+ D = Differential (t)
88
+ eqs = [0 ~ μ - x^ 3 + 2 x^ 2 ,
89
+ 0 ~ p* μ - y,
90
+ 0 ~ y - z]
91
+ @named nsys = NonlinearSystem (eqs, [x, y, z], [μ, p])
92
+ nsys = structural_simplify (nsys)
93
+
94
+ # Creates BifurcationProblem.
95
+ bif_par = μ
96
+ p_start = [μ => 1.0 ]
97
+ u0_guess = [x => 1.0 , y => 0.1 , z => 0.1 ]
98
+ plot_var = x;
99
+ bprob = BifurcationProblem (nsys, u0_guess, p_start, bif_par; plot_var= plot_var)
100
+
101
+ # Computes bifurcation diagram.
102
+ p_span = (- 4.3 , 12.0 )
103
+ opt_newton = NewtonPar (tol = 1e-9 , max_iterations = 20 )
104
+ opts_br = ContinuationPar (dsmin = 0.001 , dsmax = 0.05 , ds = 0.01 ,
105
+ max_steps = 100 , nev = 2 , newton_options = opt_newton,
106
+ p_min = p_span[1 ], p_max = p_span[2 ],
107
+ detect_bifurcation = 3 , n_inversion = 4 , tol_bisection_eigenvalue = 1e-8 , dsmin_bisection = 1e-9 );
108
+ bif_dia = bifurcationdiagram (bprob, PALC (), 2 , (args... ) -> opts_br; bothside= true )
109
+
110
+ # Tests that the diagram has the correct values (x = b)
111
+ all ([b. x ≈ 2 * b. param for b in bif_dia. γ. branch])
112
+
113
+ # Tests that we get two fold bifurcations at the correct positions.
114
+ fold_points = sort (getfield .(filter (sp -> sp. type == :bp , bif_dia. γ. specialpoint), :param ))
115
+ @test length (fold_points) == 2
116
+ @test fold_points ≈ [- 1.1851851706940317 , - 5.6734983580551894e-6 ] # test that they occur at the correct parameter values).
117
+ end
0 commit comments