Skip to content

Solid Earth tides + GR Schwarzschild in the high-precision propagator#110

Merged
ssmichael1 merged 13 commits into
mainfrom
feat/precision-physics
May 25, 2026
Merged

Solid Earth tides + GR Schwarzschild in the high-precision propagator#110
ssmichael1 merged 13 commits into
mainfrom
feat/precision-physics

Conversation

@ssmichael1
Copy link
Copy Markdown
Owner

Summary

Adds two precision force-model terms to the numerical propagator, both default-on, and restructures the propagation documentation around the natural SGP4 / numerical split.

Closes #16. Closes #109.

What's in the force model

  • Solid Earth tides (IERS 2010 §6.2.1 Step 1) — frequency-independent Love-number response. ≈99% of the solid-tide signal at ~5% per-step overhead. New tides module with TideModel enum (None / SolidStep1 / SolidFull), solid_tide_deltas(), and tide_accel(). Step 2 (71 frequency-dependent constituents) deferred; SolidFull currently falls back to Step 1.
  • GR Schwarzschild (IERS 2010 §10.3 Eq. 10.12, β = γ = 1) — single closed-form acceleration, ~10 flops per call. New relativity module. Lense-Thirring / de Sitter not yet implemented (sub-cm at MEO/GEO).

Both wired into all three integrator force closures (RKV, RODAS4, GJ8). STM partials skipped for both — ∂a/∂r contributions are ≲1e-12 of the J2 partial.

Settings

PropSettings::default() now ships tide_model: TideModel::SolidStep1 and use_relativistic_correction: true. Behavior change for callers with state-vector regression tests: end-state positions will shift. Pass TideModel::None and use_relativistic_correction=false to reproduce pre-0.18 numerics.

Validation

  • test_gps: residual vs ESA SP3 truth on a 1-day GPS arc dropped from 6.42 m (no tides, no GR) → 5.71 m (tides) → 1.86 m (tides + GR + refitted v0). Threshold tightened to 2.5 m, with a refitted hardcoded v0 and Cr*A/m.
  • New test_solid_tides_perturb_orbit: confirms ~0.34 m position drift at GEO over half a day from solid tides, matching M&G Table 3.1.
  • TestRelativisticCorrection and TestSolidTides Python test classes cover bindings + a functional with-vs-without diff.

Python bindings

  • satkit.tidemodel enum (none / solid_step1 / solid_full)
  • satkit.propsettings.tide_model and satkit.propsettings.use_relativistic_correction fields, exposed via kwarg ctor + getter/setter
  • .pyi stubs updated with docstrings on the new enum and fields

Documentation restructure

The monolithic docs/guide/satprop.md was split into three focused theory pages:

  • guide/forces.md — modeled forces with a summary table (now including tides + GR)
  • guide/integrators.md — RKV / GJ8, step-size selection, tolerances
  • guide/satstate.md — state vectors, STM, covariance, impulsive maneuvers

The "Learn" nav now has two top-level subsections — SGP4 Propagation (1 theory + 3 tutorials) and Numerical Propagation (4 theory + 2 tutorials) — instead of the previous flat 8-item list. Dropped the "Un-modeled Forces" section (was already stale — tides + GR just moved out of it).

Notebook updates

  • "High Precision Propagation" → renamed to "GPS Example" (it's a GPS-class orbit-determination worked example, not a generic propagation demo).
  • Narrative updated for tides + GR default-on: the empirical NTW acceleration is now framed as a catch-all for what isn't yet covered (ocean tides, box-wing SRP, albedo, Lense-Thirring / de Sitter, EOP residuals).
  • Re-executed end-to-end; headline residual unchanged (~0.38 m mean fit) but the fitted NTW components shift meaningfully, indicating the empirical now absorbs different physics.

Plus an unrelated fix to the TLE Fitting tutorial that surfaced during testing (fitresults["success"]fitresults["converged"], the actual key).

Test plan

  • cargo test --lib orbitprop:: — 56 tests pass (53 prior + 3 new in relativity/tides modules + new test_solid_tides_perturb_orbit)
  • cargo test --lib orbitprop::propagator::tests::test_gps — passes with new 2.5 m threshold
  • pytest python/test/ — 97 tests pass (94 prior + 3 new in TestRelativisticCorrection)
  • cargo fmt --all -- --check clean
  • mkdocs build — no broken links from the restructure
  • Notebook re-execution: HPP/GPS Example runs to completion with new defaults
  • Reviewer: confirm the v0 refit values in test_gps reproduce the 1.86 m max-axis residual on your machine (small SP3 / EOP drift can shift this slightly)

🤖 Generated with Claude Code

ssmichael1 and others added 13 commits May 25, 2026 08:23
New `tides` module with `TideModel`, `TideDeltas`, `solid_tide_deltas()`
and `tide_accel()`. Implements frequency-independent Love-number response
(Eq. 6.6 + 6.7) using anelastic Table 6.3 values. Not yet wired into the
propagator.

Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defaults to `TideModel::SolidStep1`. The field is plumbed through
Default and Display but not yet consumed by the propagator force model;
that wiring lands in the next commit.

Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ures

Hooks `tides::solid_tide_deltas` + `tides::tide_accel` into the RKV
(`ydot`), RODAS4 (`ydot_vec`), and Gauss-Jackson 8 (`accel_fn`) closures
in propagator.rs. Sun and Moon GCRF positions are rotated to ITRF via
the precomputed `qgcrf2itrf` quaternion; resulting tide acceleration is
rotated back to GCRF before summing.

Tide partials are omitted from the STM (∂a/∂r is ≲1e-12 of the J2
partial).

Adds `test_solid_tides_perturb_orbit`: confirms ~0.34 m position drift
over half a day at GEO from solid Earth tides, matching M&G Table 3.1
expectations.

Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a tides-off twin propagation and asserts strict improvement over
the no-tides baseline. Threshold drops from 8.0 m to 6.5 m per axis;
measured residuals over the 1-day GPS-20 arc are 5.71 m with tides
and 6.42 m without (degree-4 gravity).

Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New `satkit.tidemodel` enum (`none` / `solid_step1` / `solid_full`) and
`propsettings.tide_model` field plumbed through the kwarg constructor
and getter/setter. Default is `tidemodel.solid_step1`, matching the
Rust default.

Updates `satkit.pyi` with full docstrings on the new enum and field,
and adds four `TestSolidTides` tests covering enum members, default
value, kwarg/setter round-trip, and the with-vs-without propagation
difference at GEO. The Python `test_gps` SP3 threshold is tightened
from 8 m to 6.5 m to match the Rust side.

Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new `orbitprop::relativity` module with the IERS 2010 §10.3
Eq. 10.12 Schwarzschild (post-Newtonian, β=γ=1) acceleration. Wires it
into all three integrator force closures (RKV, RODAS4, GJ8) gated by a
new `PropSettings::use_relativistic_correction` field (default true).

GR partials are ~1e-15/m vs J2's ~1e-7/m and are skipped in the STM
update.

The GPS regression test threshold tightens from 6.5 m → 2.5 m. The
hardcoded `v0` + Cr*A/m was refitted against ESA SP3 truth using the
new default force model (tides + GR + degree-4 gravity). The previous
strict "with-tides residual < without-tides residual" assertion is
dropped from this test — once the IC is fit to the full model, the
initial state absorbs enough of the constant-shift tide signal that
toggling tides off can produce a smaller residual on this particular
arc. The independent `test_solid_tides_perturb_orbit` test still
guards that tides change the propagation.

Refs #109.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exposes the new GR Schwarzschild flag through the PyO3 binding via the
kwarg constructor and getter/setter. `.pyi` stub updated with the new
field, docstring, and constructor parameter.

Three new tests in `TestRelativisticCorrection`: default-on, kwarg /
setter round-trip, and a with-vs-without propagation diff at GPS
altitude.

The Python `test_gps` fitparam is refitted against SP3 truth with the
new default force model, mirroring the Rust-side refit; threshold
tightened from 6.5 m → 2.5 m.

Refs #109.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #109.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dict returned by `sk.TLE.fit_from_states` exposes `converged` as
its boolean status key (per `python/src/pytle.rs:273-285`), not
`success`. The tutorial printed `fitresults["success"]` and raised a
KeyError before reaching the final result. Switched to `converged`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cell 6: revise the empirical-acceleration explanation to enumerate the
modelled forces (now including solid Earth tides and GR Schwarzschild)
and re-frame the empirical as a catch-all for what's *not* yet covered
(ocean tides, higher-order SRP, Earth albedo, Lense-Thirring/de Sitter,
EOP residuals).

Re-executed end-to-end against the SP3 truth files. Headline residuals
similar to before (0.376 m mean fit; max 0.770 m), but the fitted NTW
empirical components shift meaningfully — the radial (N) and cross-track
(W) absorbers compensate for different missing physics now that tides
and GR are explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits the monolithic `docs/guide/satprop.md` into three theory pages
along its natural cleavage planes:

- `forces.md` — modeled forces (now including solid Earth tides and GR
  Schwarzschild), with a summary table giving the typical magnitude and
  the setting that controls each.
- `integrators.md` — RKV / GJ8 family, step-size selection, tolerances.
- `satstate.md` — state vectors, state transition matrix, covariance,
  impulsive maneuvers.

Reorganises the "Learn" section so SGP4 and numerical propagation live
in their own top-level subsections instead of a flat 8-item list. New
nav order:

  - SGP4 Propagation: theory + 3 tutorials
  - Numerical Propagation: 4 theory pages + 2 tutorials

Drops the "Un-modeled Forces" subsection — that list went stale this
week (solid tides + GR moved to "modeled"); roadmap items belong in
the issue tracker, not the docs.

API pages get a "see also" pointer at the top, and the TideModel enum
is now surfaced under `api/satprop.md`. Tutorials index rewritten to
match the new two-paradigm split.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The notebook is specifically a GPS-class orbit-determination worked
example (fit IC + Cr*A/m + NTW empirical against ESA SP3 truth, then
integrator comparison). The old title was generic and obscured what the
example actually demonstrated.

Updates the notebook's H1, the nav entry, and every cross-link in the
guide pages, API page, and tutorials index. Notebook re-execution
(mkdocs-jupyter `execute: true`) preserves outputs automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ssmichael1 ssmichael1 merged commit 0c8b001 into main May 25, 2026
5 checks passed
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.

Add general-relativistic Schwarzschild acceleration to propagator Dynamic Solid Tide

1 participant