A modular, HLS-ready C++ simulation of the double pendulum using the Störmer–Verlet (second-order finite difference) integrator. Includes a serial and parallel ensemble runner and a Python visualiser.
The double pendulum consists of two point masses
Applying the Euler–Lagrange equations to the Lagrangian of the system
yields a coupled nonlinear ODE. At each instant, the angular accelerations
where
Solved analytically via Cramer's rule. Letting:
the accelerations are:
The system is chaotic for most initial conditions near the inverted
configuration (
Two schemes are applied per timestep, one for each variable type.
The central difference approximation of the second derivative,
rearranged to advance the position:
The Verlet recurrence requires
With zero initial velocities (
Störmer–Verlet applied purely to positions is symplectic — it conserves
a shadow Hamiltonian, bounding long-term energy drift. The forward Euler step
on
A single pendulum trajectory is inherently sequential (step
Each thread owns exactly one trajectory. There is no inter-thread
communication (
double-pendulum-eqn/
assets/ committed visualisation outputs
source/
include/
config.hpp compile-time constants
dp_types.hpp PendulumState, PendulumPrev (plain-old-data)
dp_solver.hpp HLS-friendly core: compute_step(), run_simulation()
dp_state_utils.hpp SolverMetrics struct, CSV helpers
dp_serial.hpp serial ensemble solver declaration
dp_parallel.hpp parallel ensemble solver declaration
runner.hpp RunnerResult, run_pendulum2d(), print_summary()
src/
dp_solver.cpp physics kernel (no STL, no dynamic memory -- HLS-ready)
dp_state_utils.cpp ensure_dir, save_trajectory_csv, save_metrics_csv
dp_serial.cpp N_TRAJ trajectories sequentially, timed
dp_parallel.cpp N_TRAJ trajectories in parallel threads, timed
runner.cpp orchestrates serial + parallel, prints summary
double_pendulum_tb.cpp main()
testbench.cpp run_testbench() -> run_pendulum2d()
visualize.py phase space, butterfly effect, timing plots
run_viz.sh bootstrap Python venv and launch visualizer
requirements.txt
Makefile
CMakeLists.txt
Requires clang++ with C++17 support.
cd source
make # compile
make run # compile and run
make clean # remove binary and output/Expected output:
[serial] Running 8 trajectories x 10000 steps...
[parallel] Running 8 trajectories x 10000 steps (8 threads)...
===== double-pendulum ensemble run summary =====
Trajectories : 8
Steps/traj : 10000
Threads(par) : 8
Serial | total=... ms compute=... ms ...
Parallel | total=... ms compute=... ms ...
Compute speedup (serial/parallel): ~8x
(comm_ns = 0 -- no inter-thread data exchange needed)
After running, the output/ directory contains:
output/
serial/
traj_0.csv .. traj_7.csv columns: step, theta1, theta2
parallel/
traj_0.csv .. traj_7.csv
serial_metrics.csv
parallel_metrics.csv
Serial and parallel files for the same trajectory index are byte-for-byte identical. This is guaranteed: each trajectory is a fully independent sequential chain with no shared mutable state between threads — identical inputs and operation order produce identical floating-point results.
./run_viz.sh # interactive (requires display)
./run_viz.sh --save # save PNGs and GIFs to output/plots/
./run_viz.sh --mode serial # serial trajectories only
./run_viz.sh --no-anim --save # static plots only| Output file | Description |
|---|---|
phase_space.png |
|
divergence.png |
$ |
timing_metrics.png |
serial vs parallel compute time and speedup vs ideal |
phase_animation_*.gif |
animated phase-space trajectories |
The core physics kernel (dp_solver.cpp, dp_types.hpp) is written
to be HLS-compatible:
- No STL containers or dynamic memory (
std::vector,new,mallocabsent) - No HLS pragmas yet; these will be added in Vivado/Vitis HLS
- All state passed by reference as plain structs, allocated on the stack
- The ensemble runner (
dp_serial,dp_parallel,runner) uses STL and is host-side infrastructure only
Defined in include/config.hpp:
| Constant | Value | Description |
|---|---|---|
DP_G |
9.8 | gravitational acceleration ( |
DP_L1 |
2.0 | length of pendulum 1 ( |
DP_L2 |
1.0 | length of pendulum 2 ( |
DP_M1 |
1.0 | mass of bob 1 ( |
DP_M2 |
2.0 | mass of bob 2 ( |
DP_DT |
0.001 | timestep |
DP_N_STEPS |
10000 | steps per trajectory |
DP_N_TRAJ |
8 | ensemble size |
DP_IC_STEP |
0.01 | per-trajectory |



