Hi!
I have a question about the integrators in the differential equation community. The integrators for DynamicalODEProblem
are listed in Dynamical, Hamiltonian, and 2nd Order ODE Solvers · DifferentialEquations.jl. While testing for the integrators, especially those symplectic ones, I found some puzzling results regarding energy conservation:
# Tracing charged particle in a static EM field.
using TestParticle
using OrdinaryDiffEq
using StaticArrays
using LinearAlgebra: ×
#using Plots
const B₀ = 1e-8 # [T]
function location!(dx, x, v, p::TestParticle.TPTuple, t)
dx .= v
end
function lorentz!(dv, x, v, p::TestParticle.TPTuple, t)
q, m, E, B = p
dv .= q/m*(E(x, t) + v × (B(x, t)))
end
## Initialize field
function uniform_B(x)
return SA[0, 0, B₀]
end
function uniform_E(x)
return SA[0.0, 0.0, 0.0]
end
"Check energy conservation."
function getenergy(sol)
vx, vy, vz = sol.u[1][4:6]
KE1 = √(vx^2 + vy^2 + vz^2)
vx, vy, vz = sol.u[end][4:6]
KE2 = √(vx^2 + vy^2 + vz^2)
KE1, KE2
end
## Initialize particles
x0 = [0.0, 0, 0]
v0 = [0.0, 1e2, 0.0]
stateinit = [x0..., v0...]
tspan_proton = (0.0, 2000.0)
param_proton = prepare(uniform_E, uniform_B, species=Proton)
## Solve for the trajectories
prob_p = DynamicalODEProblem(location!, lorentz!, x0, v0, tspan_proton, param_proton)
Ωᵢ = TestParticle.qᵢ * B₀ / TestParticle.mᵢ
Tᵢ = 2π / Ωᵢ
println("Number of gyrations: ", tspan_proton[2] / Tᵢ)
function main(prob_p)
@info "Tsit5"
@time sol_p = solve(prob_p, Tsit5(); save_idxs=[1,2,3,4,5,6]);
KE1, KE2 = getenergy(sol_p)
println("Energy: ", KE1, " to ", round(KE2, digits=4))
@info "KahanLi6"
@time sol_p = solve(prob_p, KahanLi6(); dt=0.01, save_idxs=[1,2,3,4,5,6]);
KE1, KE2 = getenergy(sol_p)
println("Energy: ", KE1, " to ", round(KE2, digits=4))
@info "Trapezoid"
@time sol_p = solve(prob_p, Trapezoid(); save_idxs=[1,2,3,4,5,6]);
KE1, KE2 = getenergy(sol_p)
println("Energy: ", KE1, " to ", round(KE2, digits=4))
@info "Vern6"
@time sol_p = solve(prob_p, Vern6(); save_idxs=[1,2,3,4,5,6]); # higher accuracy than Tsit5
KE1, KE2 = getenergy(sol_p)
println("Energy: ", KE1, " to ", round(KE2, digits=4))
end
main(prob_p)
#=
# Visualization
xyz = plot(sol_p, vars=(1,2,3), xlabel="x", ylabel="y", zlabel="z",
aspect_ratio=:equal, label="proton", lw=2)
=#
julia> main(prob_p);
[ Info: Tsit5
0.010167 seconds (61.31 k allocations: 3.842 MiB)
Energy: 100.0 to 109.8673
[ Info: KahanLi6
0.458471 seconds (2.80 M allocations: 237.040 MiB, 50.05% gc time)
Energy: 100.0 to 887502.8753
[ Info: Trapezoid
0.082830 seconds (1.05 M allocations: 25.859 MiB)
Energy: 100.0 to 100.5663
[ Info: Vern6
0.004061 seconds (51.38 k allocations: 3.406 MiB)
Energy: 100.0 to 97.2829
This shows that the symplectic integrator KahanLi6
does not conserve energy in my case, while others perform reasonably well (< 10% energy change) for a single proton that gyrates for 300 periods in a static magnetic field. Did I set something wrong when using the symplectic integrators?