Skip to content

Release GIL in Python bindings; simplification cleanups; criterion benchmark suite#119

Open
ssmichael1 wants to merge 3 commits into
mainfrom
simplify-cleanups
Open

Release GIL in Python bindings; simplification cleanups; criterion benchmark suite#119
ssmichael1 wants to merge 3 commits into
mainfrom
simplify-cleanups

Conversation

@ssmichael1

Copy link
Copy Markdown
Owner

Summary

Three threads of work, no behavior changes to numerical results:

Python bindings release the GIL during long computations

  • propagate, TLE.fit_from_states, sgp4 (all three input forms), and every array-valued frame-transform / ephemeris helper now run detached (Python::detach), so other Python threads make progress during multi-second propagations and large batch transforms.
  • SGP4 paths clone the TLE, compute detached, and write back the cached init state. The batch (list) path extracts TLE/OMM sources with the GIL held, computes detached, then writes the TLEs back.
  • Verified empirically: a competing Python thread ran 115M loop iterations during a 1.8 s qgcrf2itrf call over 100k times (previously it would have been blocked the entire time).

Simplification cleanups

  • New earth_orientation_params::get_or_zero / eop_from_mjd_utc_or_zero replace six copies of .unwrap_or([0.0; 6]).
  • Removed the dead legacy finals2000A.all EOP loader and its three orphaned error variants.
  • lpephem::moon::phase wrap simplified to rem_euclid (the > 2π branch was unreachable); 2.0 * PI literals → std::f64::consts::TAU.
  • Shared reject_unused_kwargs helper replaces three hand-rolled extraneous-kwargs folds.
  • quaternion / itrfcoord / kepler pickle impls share pack_f64s/unpack_f64s; wire format is byte-identical, old pickles load unchanged.
  • The vec3 array helpers lose their per-element unsafe pointer copies.
  • Deleted stray scratch files from the repo root.

Two deliberate behavior changes (both changelogged):

  • duration(day=1) (typo'd kwargs) now raises ValueError — previously silently ignored, returning a zero duration.
  • propsettings invalid kwargs raise ValueError instead of RuntimeError, for consistency.

Criterion benchmark suite

benches/hotpaths.rs (cargo bench) baselines the hot paths: propagation per regime/integrator, SGP4 cold + cached, frame transforms, spherical-harmonic gravity, JPL ephemeris, NRLMSISE-00. Key numbers (M-series macOS): LEO+drag 1-day 12.9 ms, +STM 33.8 ms, GEO 685 µs, SGP4 ~280 ns/step, exact qgcrf2itrf 18.9 µs vs 159 ns approx.

Test plan

  • cargo test: 190 lib + 41 integration tests pass
  • pytest python/test/: 97 tests pass
  • GIL-release concurrency smoke test (spin thread vs 100k-time transform)
  • Pickle round-trips verified for quaternion/itrfcoord/kepler; sgp4 single/list paths verified
  • cargo bench --bench hotpaths -- --quick runs clean

🤖 Generated with Claude Code

ssmichael1 and others added 3 commits June 9, 2026 21:30
Python bindings:
- propagate, TLE.fit_from_states, sgp4 (all input forms), and the
  array-valued frame-transform/ephemeris helpers now run detached
  (Python::detach) so other Python threads progress during long calls
- sgp4 paths clone the TLE, compute detached, and write back the
  cached SGP4 init state; batch list path extracts sources under the
  GIL and computes detached
- shared reject_unused_kwargs helper replaces three hand-rolled
  extraneous-kwargs folds; duration() now rejects unknown kwargs
  instead of silently ignoring them
- quaternion/itrfcoord/kepler pickle impls share pack_f64s/unpack_f64s
  (wire format unchanged); vec3 array helpers drop per-element unsafe
  pointer copies

Rust core:
- earth_orientation_params::get_or_zero / eop_from_mjd_utc_or_zero
  helpers replace repeated .unwrap_or([0.0; 6]) pattern
- remove dead legacy finals2000A.all EOP loader and its error variants
- lpephem::moon::phase wrap via rem_euclid (dead branch removed);
  use std::f64::consts::TAU for 2*pi literals
- delete stray scratch files test.py / test2.py

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
benches/hotpaths.rs covers the per-call costs that dominate real
workloads: high-precision propagation (LEO drag / GEO RKV98, GEO
Gauss-Jackson 8, LEO with STM), SGP4 cold + cached over 1 day at
1-minute cadence, frame transforms (exact and approx), spherical
harmonic gravity (degree 4/16, with partials), JPL ephemeris lookups,
and NRLMSISE-00 density.

Baseline (M-series macOS, --quick):
- propagation/leo_drag_1day_rkv98      12.9 ms
- propagation/leo_stm_1day_rkv98       33.8 ms
- propagation/geo_1day_rkv98            685 us
- propagation/geo_1day_gj8              494 us
- sgp4 1-day @ 1-min (1441 pts)         404 us  (~280 ns/step)
- frametransform/qgcrf2itrf            18.9 us
- frametransform/qgcrf2itrf_approx      159 ns
- earthgravity/accel_deg4                85 ns
- jplephem/moon_geocentric_pos           24 ns
- nrlmsise/density_400km                1.5 us

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a 'Local Tangent Planes and Working with Many Coordinates' section
to the Geodetic Coordinates tutorial: scalar to_enu/to_ned usage (which
the intro promised but no section covered), the one-rotation numpy
matmul pattern for batch ENU/NED (~15 ms/million points vs ~1 s/million
looped), and the per-point comprehension pattern for Cartesian ->
geodetic. Documents that batched itrfcoord inputs are deliberately not
part of the API (context: issue #118).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant