aboutsummaryrefslogtreecommitdiff
path: root/hw3/Hamiltonian.jl
blob: 7cefe55132f8c51c015050ab879c2622c43b5f5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# FOR PHYS2600 HW3
# author: sotech117

using DifferentialEquations
using Plots

g = 9.8
t_span = (0.0, 1000.0)
p = g

function tendency!(du, u, p, t)
    q_1, q_2, p_1, p_2 = u
    g = p

    denominator = 1.0 + (sin(q_1 - q_2)*sin(q_1 - q_2))

    dq_1 = (p_1 - p_2 * cos(q_1 - q_2)) / denominator
    dq_2 = (-p_1*cos(q_1 - q_2) + 2*p_2) / denominator

    k_1 = p_1*p_2*sin(q_1 - q_2) / denominator
    k_2 = (p_1*p_1 + 2*p_2*p_2 - 2*p_1*p_2*cos(q_1 - q_2)) / (2*denominator*denominator)

    dp_1 = -2 * sin(q_1) - k_1 + k_2 * sin(2*(q_1 - q_2))
    dp_2 = - sin(q_2) + k_1 - k_2 * sin(2*(q_1 - q_2))
    
    du[1] = dq_1
    du[2] = dq_2
    du[3] = dp_1
    du[4] = dp_2
end

function energy(u, p)
    q_1, q_2, p_1, p_2 = u
    g = p

    T = 0.5 * (p_1*p_1 + p_2*p_2)
    V = - 2 * cos(q_1) - cos(q_2)
    return T + V
end

function build_ensemble(q_start, q_end, p_start, p_end, n)
    q_1s = zeros(n)
    q_2s = zeros(n)
    p_1s = zeros(n)
    p_2s = zeros(n)
    for i in 1:n
        q_1s[i] = q_start + (q_end - q_start) * rand()
        q_2s[i] = q_start + (q_end - q_start) * rand()
        p_1s[i] = p_start + (p_end - p_start) * rand()
        p_2s[i] = p_start + (p_end - p_start) * rand()
    end
    return [q_1s, q_2s, p_1s, p_2s]
end

# take a list and reduce theta to the interval [-π, π]
function clean_θ(θ::Vector{Float64})
     = []
    for i in 1:length(θ)
        tmp = θ[i] % (2 * π)
        if tmp > π
            tmp = tmp - 2 * π
        elseif tmp < -π
            tmp = tmp + 2 * π
        end
        push!(, tmp)
    end
    return 
end

function run_simulation(u_0, tspan, p)
    prob = ODEProblem(tendency!, u_0, tspan, p)
    sol = solve(prob, Tsit5(), reltol=1e-8, abstol=1e-8) # control simulation
    sol[1, :] = clean_θ(sol[1, :])
    sol[2, :] = clean_θ(sol[2, :])
    return sol
end

# returns the new ensemble after t_span time
function simulate_ensembles(ensemble, tspan, p)
    q_1s = zeros(length(ensemble[1]))
    q_2s = zeros(length(ensemble[2]))
    p_1s = zeros(length(ensemble[3]))
    p_2s = zeros(length(ensemble[4]))
    println("length ensemble = ", length(ensemble[1]))
    for i in 1:length(ensemble[1])
        u_0 = [ensemble[1][i], ensemble[2][i], ensemble[3][i], ensemble[4][i]]
        sol = run_simulation(u_0, tspan, p)

        q_1s[i] = sol[1, end]
        q_2s[i] = sol[2, end]
        p_1s[i] = sol[3, end]
        p_2s[i] = sol[4, end]
    end
    println("q1_s", length(q_1s))
    return [q_1s, q_2s, p_1s, p_2s]
end

function sum_all_positions_and_momenta(ensemble)
    q_1 = 0
    q_2 = 0
    p_1 = 0
    p_2 = 0
    for i in 1:length(ensemble[1])
        q_1 += ensemble[1][i]
        q_2 += ensemble[2][i]
        p_1 += ensemble[3][i]
        p_2 += ensemble[4][i]
    end
    return [q_1, q_2, p_1, p_2]
end

function estimate_phase_space_volume(ensemble, n)
    # make 100000 random points in the 4d phase space from [-pi, pi] x [-pi, pi] x [-3, 3] x [-3, 3]
    points = build_ensemble(-pi, pi, -3, 3, 100000)

    # iterate over the points and find the number of points that hit the ensemble
    count = 0
    for i in 1:length(points[1])
        for j in 1:length(ensemble[1])
            if abs(points[1][i] - ensemble[1][j]) < n && abs(points[2][i] - ensemble[2][j]) < n && abs(points[3][i] - ensemble[3][j]) < n && abs(points[4][i] - ensemble[4][j]) < n
                count += 1
                break
            end
        end
    end

    # return the volume
    return (count / 100000) # note units don't matter, just comparing
end


# PROBLEM 1 -> show that two systems with the same intial energy can have different chaotic results
u_1_0 = [0, 0, 6.26, 0.0]
u_2_0 = [0.0, 0.0, 0.0, 6.26]

# print the initial energies
println("energy 1 = ", energy(u_1_0, p))
println("energy 2 = ", energy(u_2_0, p))

sol_1 = run_simulation(u_1_0, t_span, p)
sol_2 = run_simulation(u_2_0, t_span, p)

# plot the phase space
p1 = scatter(sol_1[1, :], sol_1[3, :], label="q_1", xlabel="q_1 (radians)", ylabel="p_1 (momentum)", title="Phase Space i=1 for Pendulum 1", legend=false, color=:blue, msw=0, ms=.75)
p2 = scatter(sol_2[1, :], sol_2[3, :], label="q_2", xlabel="q_1 (radians)", ylabel="p_1 (momentum)", title="Phase Space i=1 for Pendulum 2", legend=false, color=:red, msw=0, ms=.75)

# plot the other phase space
p3 = scatter(sol_1[2, :], sol_1[4, :], label="q_1", xlabel="q_2 (radians)", ylabel="p_2 (momentum)", title="Phase Space i=2 for Pendulum 1", legend=false, color=:blue, msw=0, ms=.75)
p4 = scatter(sol_2[2, :], sol_2[4, :], label="q_2", xlabel="q_2 (radians)", ylabel="p_2 (momentum)", title="Phase Space i=2 for Pendulum 2", legend=false, color=:red, msw=0, ms=.75)

# plot the trajectories
p5 = scatter(sol_1[1, :], sol_1[2, :], label="q_1 (radians)", xlabel="q_1 (radians)", ylabel="q_2 (radians)", title="Relative Trajectory for Pend1", legend=false, color=:blue, msw=0, ms=.75)
p6 = scatter(sol_2[1, :], sol_2[2, :], label="q_2 (radians)", xlabel="q_1 (radians)", ylabel="q_2 (radians)", title="Relative Trajectory for Pend2", legend=false, color=:red, msw=0, ms=.75)

# plot the trajectories over time
p7 = plot(sol_1.t, sol_1[1, :], label="pendulum 1", xlabel="time (s)", ylabel="q_1 (radians)", title="q_1 vs time", legend=:topright)
plot!(p7, sol_2.t, sol_2[1, :], label="pendulum 2", xlabel="time (s)", ylabel="q_1 (radians)", title="q_1 vs time")

p8 = plot(sol_1.t, sol_1[2, :], label="pendulum 1", xlabel="time (s)", ylabel="q_2 (radians)", title="q_2 vs time", legend=:topright)
plot!(p8, sol_2.t, sol_2[2, :], label="pendulum 2", xlabel="time (s)", ylabel="q_2 (radians)", title="q_2 vs time")

plt = plot(p1, p2, p3, p4, p5, p6, p7, p8, layout=(4, 2), size=(1000, 1000))

savefig(plt, "hw3/double_pendulum.png")

# PROBLEM 2 -> show that the volume of the phase space is conserved

ensemble = build_ensemble(-1, 1, -1, 1, 1000)
# plot these points
s1 = scatter(ensemble[1], ensemble[3], label="t=0", xlabel="q_1 (radians)", ylabel="p_1 (momentum)", title="Phase Space (i=1)", color=:blue, msw=.5)
s2 = scatter(ensemble[2], ensemble[4], label="t=0", xlabel="q_2 (radians)", ylabel="p_2 (momentum)", title="Phase Space (i=2)", color=:red, msw=.5)
# simulate the ensemble
new_ensemble = simulate_ensembles(ensemble, t_span, p)
# plot these points
scatter!(s1, new_ensemble[1], new_ensemble[3], label="t=1000", xlabel="q_1 (radians)", ylabel="p_1 (momentum)", title="Phase Space (i=1)", color=:green, msw=.5)
scatter!(s2, new_ensemble[2], new_ensemble[4], label="t=1000", xlabel="q_2 (radians)", ylabel="p_2 (momentum)", title="Phase Space (i=2)", color=:green, msw=.5)

# print the volumes
println("\n\nOriginal Volume:\t", estimate_phase_space_volume(ensemble, .1))
println("Final Volume:\t", estimate_phase_space_volume(new_ensemble, .1))

plt_2 = plot(s1, s2)
savefig(plt_2, "hw3/ensemble.png")