Mercurial > lbo > hg > lattice2d
view linear_chain.jl @ 10:9356247f3999 default tip
Update code for publishing article at lewinb.net
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Wed, 19 Jan 2022 22:51:26 +0100 |
parents | a9a74f7cee66 |
children |
line wrap: on
line source
import LinearAlgebra using DifferentialEquations using Plots theme(:ggplot2) # The coupling matrix has 4nx4n elements for a linear chain of two degrees of freedom per oscillator. # We use Born-von Karman boundary conditions, i.e. the chain is a ring. function n_coupled_masses(n::Int, ks::Vector, masses::Vector; fixed_ends=false) # Prepares coupling matrix. if length(ks) != (n+1) @show (length(ks), n+1) error("Length of ks must be n+1.") end if length(masses) != n @show n error("Length of masses must be n.") end A = zeros(4n, 4n) A[1:2n, 2n+1:4n] = LinearAlgebra.I(2n) for i = 1:n # oscillators k1, k2 = ks[i], ks[i+1] x = 2i-1 y = 2i # Link previous oscillator prevx = (x-2) < 1 ? (2n-1) : (x-2) prevy = (y-2) < 1 ? (2n) : (y-2) if !fixed_ends || ((x-2) >= 1) A[2n+x, prevx] += k1/masses[i] end if !fixed_ends || ((y-2) >= 1) A[2n+y, prevy] += k1/masses[i] end # Link next oscillator nextx = ((x+2)%(2n)) nexty = max(((y+2-1)%(2n)+1), 2) if !fixed_ends || (nextx == x+2) A[2n+x, nextx] += k2/masses[i] end if !fixed_ends || (nexty == y+2) A[2n+y, nexty] += k2/masses[i] end A[2n+x, x] -= (k1+k2)/masses[i] A[2n+y, y] -= (k1+k2)/masses[i] end A end # The state vector has 4n elements: 2n elements containing x/y positions (alternating, 2i-1 is x), # and another 2n elements containing x/y velocities (alternating). # The initial displacements are given as (i, x, y) tuples representing a # (x,y) displacement of the i'th oscillator. function initial_state(n::Int, displacements::Vector)::Vector{Float64} u0 = zeros(4n) for (di, dx, dy) in displacements u0[2di-1] += dx u0[2di] += dy end u0 end # The differential equation. function f(du::Vector{Float64}, u::Vector{Float64}, A, t) # u has N positions, N velocities. # A gives: du/dt = A u du .= A * u end # Find eigenmodes of system. function eigenmodes(n::Int, coupling::Matrix) sub = @view coupling[2n+1:end, 1:2n] LinearAlgebra.eigen(sub) end # Convert an eigenvector E.vectors[:,i] into a system state. function initial_state_from_eigenmode(em::Vector)::Vector vcat(em, zeros(size(em))) end # Animate a solution of the system. function plot_chain_animation(n::Int, solution) xs = collect(1:n) ys = zeros(n) anim = @animate for (i,u) in enumerate(solution.u) pos = reshape(u[1:2n], 2, n) scatter(xs .+ pos[1,:], pos[2,:], xlim=(-1, N+1), ylim=(-1, 1)) end gif(anim, "anim_$(n).gif") end function plot_website_chain_animation(n::Int, solution) xs = .4 .* collect(1:n) ys = zeros(n) T = length(solution.u) trajectories_x, trajectories_y = zeros(n, T), zeros(n, T) anim = @animate for (i,u) in enumerate(solution.u) xpos = u[1:2:2n] ypos = u[2:2:2n] trajectories_x[:, i] = xs .+ xpos trajectories_y[:, i] = ypos .+ ones(n) plot(vcat([0], xs .+ xpos, [(n+1)*.4]), ones(n+2) .+ vcat([0], ypos, [0]), xlim=(0, .4*(n+1)), ylim=(.8, 1.2), size=(800, 200), legend=false, markersize=8, markershape=:circle) plot!(trajectories_x[:, 1:i]', trajectories_y[:, 1:i]') end gif(anim, "anim_$(n).gif", fps=12) end function initial_wave_displacement(N::Int, q::Float64, ampl::Float64=.25, a::Float64=1.; trans=false)::Vector{Tuple} N = div(N, 2) ampls = [ampl * sin(q * a * i) for i in 1:N] [(i, trans ? 0. : ampls[i], trans ? ampls[i] : 0.) for i in 1:N] end N = 20 k1, k2 = 1., 2. mass = 1. initial_displacement = [(1, 0.15, 0.15), (N, 0., 0.)] initial_displacement_2 = [(1, .5, 0.)] initial_wave_4 = initial_wave_displacement(N, 2pi/(N), .1; trans=true) timespan = (0, 90) coupling_H = n_coupled_masses(N, k1 * ones(N+1), ones(N) * mass; fixed_ends=true) E = eigenmodes(N, coupling_H) # Three different initial configurations as choice. u0 = initial_state(N, initial_displacement) u1 = initial_state_from_eigenmode(E.vectors[:,1] ./ 3) u2 = initial_state(N, initial_wave_4) problem = ODEProblem(f, u0, timespan, coupling_H); @time solution = solve(problem); # 3D trajectory plot # plot(solution, vars=[(0, 2x-1, 2x) for x in 1:N], # size=(900,450), title="x trajectories") # savefig("trajectory_3d_$(N).svg") # phase image # plot(solution, vars=[(1,2), (3,4), (5,6), (7,8), (9,10)]) # savefig("phasefig_$(N).svg") # plot(solution, vars=[(0, 2i-1) for i in 1:N]) # Animation plot_chain_animation(N, solution)