0.1.2 Numba optimizations for faster simulation (2026-04-15)¶
The math helpers were greatly improved! Almost every core function in
ADCS/helpers/math_helpers.py now has a @njit(cache=True)
implementation, so the heavy lifting happens in compiled machine code rather
than interpreted Python. First-call JIT compile time is paid once per session
(or skipped entirely on subsequent runs thanks to the cache), and from there
the speedups are free.
New Features¶
Added
numba-accelerated paths for the main math helper functions.Added dependency:
numba==0.65.0.
What Changed Under the Hood¶
Most of the work was inside math_helpers.py. The key idea is that
@njit functions can’t use arbitrary Python objects, so each helper was
rewritten to work purely with NumPy arrays and scalars
Here’s a quick tour of what got changed:
Quaternion algebra — mrp_to_quat, cayley_to_quat,
quat_to_mrp, quat_to_cayley, quat_to_vec3, vec3_to_quat,
quat_inv, quat_diff, rot_exp, and dcm_to_quat all now
pre-allocate output arrays with np.empty and fill them element-by-element
rather than building intermediate lists. The mrp_to_quat rewrite is
worth calling out specifically: the old path computed atan(||mrp||) and
then normalized, which is numerically equivalent but more expensive than the
direct closed-form (1 − s^2)/(1 + s^2) identity used now.
Dynamics kernels — Three new private @njit helpers handle the
satellite dynamics inner loop:
_quat_qdot(w, q)— quaternion kinematics (replaces0.5 * w @ Wmat(q).T)_wdot_no_rw_kernel(w, total_torque, J, invJ_noRW)— angular acceleration without reaction wheels_wdot_rw_kernel(w, h, total_torque, J, invJ_noRW, rw_axes)— angular acceleration with reaction wheels_rw_hdot_kernel(u_rw, wdot, rw_axes, rw_js)— reaction wheel momentum derivative
These are called directly from satellite.py, replacing the inline
np.cross / np.diagflat expressions that couldn’t be JIT-compiled.
Norm and Jacobian utilities: norm now uses a manual sqrt(sum of
squares) loop (faster under njit than np.linalg.norm). normalize
pre-allocates its output. The Jacobian helpers normed_vec_jac,
vec_norm_jac, and vec_norm_hess each got a @njit inner kernel with a thin
Python wrapper to preserve the optional-dv API.
Quaternion multiply: A new _quat_mult_2 kernel handles the two-
quaternion case. The public quat_mult wrapper stays pure Python so it
can still accept variable-length argument lists, but the inner multiply is
now compiled.
``rot_mat``: Not JIT-compiled, but its formatting was cleaned up.
Reaction wheel actuator: Bias and noise states are now updated before their values are read, so the actuator output always reflects the current step rather than the previous one.
Benchmark improvements: benchmark_planner.py now has a --warmup
CLI flag. Warmup runs trigger Numba’s JIT compilation before the
timed window opens, so benchmark numbers reflect steady-state performance
rather than the first-call compilation penalty.
Benchmark Results¶
Benchmarks were run on the five main tutorial scripts. The warmup run was excluded from all timings.
Benchmark Script |
Main (Avg) |
Numba (Avg) |
Percent Faster |
|---|---|---|---|
01_underactuated_control.py |
54.303 s |
37.706 s |
30.56% |
02_noisy_control.py |
31.660 s |
31.626 s |
0.11% |
03_simple_estimation.py |
55.932 s |
48.000 s |
13.09% |
04_complex_estimation.py |
76.758 s |
72.910 s |
5.01% |
05_orbit_estimation.py |
9.002 s |
8.407 s |
6.61% |
Overall Average |
45.369 s |
39.766 s |
12.35% |