| name | hamiltonian-mechanics |
| description | Hamiltonian mechanics — symplectic integrators (leapfrog, Yoshida), Hamilton's equations, Poisson brackets, canonical transformations, action-angle variables, and KAM theory analysis. Use for energy-conserving long-time integration of conservative systems. |
| category | physics |
| version | 1.0.0 |
| author | Synthetic Sciences |
| license | MIT |
| tags | ["Hamiltonian","Symplectic","Leapfrog","Classical Mechanics","Energy Conservation","Physics"] |
| dependencies | ["scipy>=1.11.0","numpy>=1.24.0","matplotlib>=3.7.0"] |
Hamiltonian Mechanics
Overview
Solve Hamilton's equations using symplectic integrators that exactly conserve the symplectic structure and approximately conserve energy for exponentially long times. Essential for N-body problems, celestial mechanics, molecular dynamics, and any conservative system needing long-time accuracy.
When to Use
- Conservative (energy-preserving) systems
- Long-time integration (thousands of orbits, molecular dynamics)
- N-body gravitational or Coulomb problems
- When energy drift from standard RK45 is unacceptable
- Phase space structure analysis (KAM tori, resonances)
Do NOT Use When
- System has dissipation (use
ode-solver with RK45/Radau)
- System is stiff (symplectic methods are explicit → CFL-limited)
- You need adaptive time-stepping (symplectic methods use fixed dt)
Core Workflows
1. Leapfrog / Stormer-Verlet (2nd Order Symplectic)
import numpy as np
import matplotlib.pyplot as plt
def leapfrog(dH_dq, dH_dp, q0, p0, dt, n_steps):
"""
Leapfrog (Stormer-Verlet) symplectic integrator.
H(q, p) is the Hamiltonian.
dH_dq = ∂H/∂q (returns force: dp/dt = -∂H/∂q)
dH_dp = ∂H/∂p (returns velocity: dq/dt = ∂H/∂p)
"""
n = len(q0)
q = np.zeros((n_steps + 1, n))
p = np.zeros((n_steps + 1, n))
q[0] = q0
p[0] = p0
for i in range(n_steps):
p_half = p[i] - 0.5 * dt * dH_dq(q[i], p[i])
q[i+1] = q[i] + dt * dH_dp(q[i], p_half)
p[i+1] = p_half - 0.5 * dt * dH_dq(q[i+1], p_half)
return q, p
():
r = np.linalg.norm(q)
q / r**
():
p
q0 = np.array([, ])
p0 = np.array([, ])
dt =
n_steps =
q, p = leapfrog(dH_dq, dH_dp, q0, p0, dt, n_steps)
H = * np.(p**, axis=) - / np.linalg.norm(q, axis=)
()
fig, axes = plt.subplots(, , figsize=(, ))
axes[].plot(q[:, ], q[:, ], , linewidth=)
axes[].plot(, , , markersize=, label=)
axes[].set_xlabel()
axes[].set_ylabel()
axes[].set_title()
axes[].set_aspect()
axes[].legend()
axes[].grid(, alpha=)
t = np.arange(n_steps + ) * dt
axes[].plot(t, (H - H[])/(H[]), , linewidth=)
axes[].set_xlabel()
axes[].set_ylabel()
axes[].set_title()
axes[].grid(, alpha=)
plt.tight_layout()
plt.savefig(, dpi=, bbox_inches=)