rand_normal01() = rand(Normal(0, 1)) function update_verlet_list!(args) @simd for pv in args.verlet_list reset!(pv) end for i in 1:(args.N - 1) for j in (i + 1):args.N p1 = args.particles[i] p2 = args.particles[j] overlapping, r⃗₁₂, distance² = are_overlapping(p1, p2, args.skin_r², args.l) if overlapping push!(args.verlet_list[i], j) push!(args.verlet_list[j], i) end end end end function euler!(args) Threads.@threads for p in args.particles verlet_list = args.verlet_list[p.id] for i in 1:verlet_list.last_ind p2 = args.particles[verlet_list.v[i]] overlapping, r⃗₁₂, distance² = are_overlapping(p, p2, args.interaction_r², args.l) if overlapping c = args.c₁ / (distance²^4) * (args.c₂ / (distance²^3) - 1) @simd for j in 1:2 p.tmp_c[j] -= c * r⃗₁₂[j] end end end e = SVector(cos(p.φ), sin(p.φ)) @simd for i in 1:2 p.tmp_c[i] += args.vδt * e[i] + args.c₃ * rand_normal01() end p.φ += args.c₄ * rand_normal01() restrict_coordinates!(p; l=args.l) end @simd for particle in args.particles update!(particle) end return nothing end function simulate(args, save_at::Float64, δt::Float64, T::Float64, n_steps_before_verlet_list_update::Int64) sol = Solution(args.N, args.n_frames) save_timer = save_at frame = 0 start_time = now() println("Started simulation at $start_time.") update_verlet_list_at = n_steps_before_verlet_list_update * δt update_verlet_list_timer = update_verlet_list_at @showprogress 0.2 for t in 0:δt:T if save_timer >= save_at frame += 1 sol.t[frame] = t @simd for p in args.particles p_id = p.id pre_pos = sol.position[p_id, frame] @simd for i in 1:2 pre_pos[i] = p.c[i] end sol.φ[p_id, frame] = p.φ end save_timer = 0.0 else save_timer += δt end if update_verlet_list_timer >= update_verlet_list_at update_verlet_list!(args) update_verlet_list_timer = 0.0 else update_verlet_list_timer += δt end euler!(args) end end_time = now() elapsed_time = canonicalize(CompoundPeriod(end_time - start_time)) println("Done simulation at $end_time and took $elapsed_time.") return (sol, end_time) end