Skip to content

Pvar-dissip: NonInertialFrameForce rectangular Jacobians in C#935

Open
jobovy wants to merge 1 commit into
feat/pvar-dissip-structfrom
feat/pvar-tail-noninertial
Open

Pvar-dissip: NonInertialFrameForce rectangular Jacobians in C#935
jobovy wants to merge 1 commit into
feat/pvar-dissip-structfrom
feat/pvar-tail-noninertial

Conversation

@jobovy

@jobovy jobovy commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Stacked on #933. Exact closed-form frame Jacobians: dF/dv = −2[Ω]ₓ, dF/dx = −[Ω]ₓ[Ω]ₓ − [Ω̇]ₓ (all Omega configurations the C force supports, incl. time-dependent/cinterp; translation terms zero).

Key physics test: tr(∂F/∂v)=0 ⇒ the rotating frame is phase-volume preserving — det M(t)=1 holds despite velocity dependence, exercising the velocity block in a way friction cannot. Plus Jacobian-vs-FD and FD-of-flow. Regression 126→131 (=+5 new); test_noninertial.py 53 unchanged. Verifier: pass.

numpy-path changes: none (dxdv path previously unavailable).

🤖 Generated with Claude Code

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 8.86700% with 185 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.89%. Comparing base (8f50486) to head (8b4bc77).
⚠️ Report is 2 commits behind head on feat/pvar-dissip-struct.

Files with missing lines Patch % Lines
...ential_c_ext/ChandrasekharDynamicalFrictionForce.c 0.00% 76 Missing ⚠️
.../potential/potential_c_ext/NonInertialFrameForce.c 0.00% 69 Missing ⚠️
galpy/orbit/orbit_c_ext/integrateFullOrbit.c 10.34% 26 Missing ⚠️
galpy/potential/potential_c_ext/galpy_potentials.c 45.45% 12 Missing ⚠️
galpy/orbit/integrateFullOrbit.py 0.00% 2 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (8f50486) and HEAD (8b4bc77). Click for more details.

HEAD has 4 uploads less than BASE
Flag BASE (8f50486) HEAD (8b4bc77)
13 9
Additional details and impacted files
@@                     Coverage Diff                      @@
##           feat/pvar-dissip-struct     #935       +/-   ##
============================================================
- Coverage                    99.92%   61.89%   -38.03%     
============================================================
  Files                          225      225               
  Lines                        36140    36325      +185     
  Branches                       778      810       +32     
============================================================
- Hits                         36113    22485    -13628     
- Misses                          27    13840    +13813     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jobovy jobovy force-pushed the feat/pvar-dissip-struct branch from 43451e7 to 85497c1 Compare June 10, 2026 17:39
@jobovy jobovy force-pushed the feat/pvar-tail-noninertial branch from 76fd6b8 to 640807e Compare June 10, 2026 17:41
@jobovy jobovy force-pushed the feat/pvar-dissip-struct branch from 85497c1 to fa982b7 Compare June 10, 2026 20:43
@jobovy jobovy force-pushed the feat/pvar-tail-noninertial branch from 640807e to 093e2fb Compare June 10, 2026 20:43
@jobovy jobovy force-pushed the feat/pvar-dissip-struct branch from fa982b7 to 862c9e5 Compare June 11, 2026 01:41
@jobovy jobovy force-pushed the feat/pvar-tail-noninertial branch from 093e2fb to 8b4bc77 Compare June 11, 2026 01:45
@jobovy jobovy force-pushed the feat/pvar-dissip-struct branch from 862c9e5 to 2e23404 Compare June 11, 2026 02:00
Wire the rectangular force Jacobian (dF/dx, dF/dv) of NonInertialFrameForce
into the RectDissipativeForceJacobian machinery of the 3D variational
equations (Orbit.integrate_dxdv): the frame force
F = -2 Omega x (v+v0) - Omega x (Omega x [r+x0]) - Omegadot x [r+x0] - a0(t)
is linear in (r, v), so the Jacobian blocks are EXACT closed forms
  dF/dv = -2 [Omega]_x
  dF/dx = |Omega|^2 I - Omega Omega^T - [Omegadot]_x
with the translation terms (x0, v0, a0) contributing zero -- for EVERY
configuration the C force supports: constant scalar Omega about z, constant
vector Omega, constant Omegadot (Omega + Omegadot t), and Omega as
function(s) of time through both the tfunc callbacks (pot_type 39) and the
cinterp GSL splines (pot_type 45; Omegadot as the spline derivative, time
queries clamped to [tmin,tmax] exactly as in the force; Omega/Omegadot and
|Omega|^2 evaluated branch-for-branch as in the force). No configuration is
unwireable, so hasC_dxdv3d=True unconditionally on the Python class
(aggregated by CompositePotential as before).

Because dF/dv = -2 [Omega]_x is antisymmetric, tr(dF/dv) = 0: rotating (even
arbitrarily time-dependent, translating) frames are phase-volume PRESERVING,
det M(t) = 1 -- exercising the velocity block of the variational Jacobian
nontrivially, unlike the contracting friction case.

Validation (orbit-level tests in tests/test_orbit.py, mirroring the
Chandrasekhar dissipative dxdv battery; they validate the C Jacobian
end-to-end against code paths independent of it):
- FD-of-the-flow STM test (MWPotential2014 in a spinning-up frame, fixed-
  args path, and in a vector-Omega(t) + translating frame via the cinterp
  splines): all 6 columns < 2.7e-6 (tolerance 1e-4).
- KEY physics test: det M(t) = 1 along the whole orbit to <= 9.2e-11
  (tolerance 1e-8) for both flow configurations -- asserted directly on the
  STM determinant, no trace integral needed since tr(dF/dv) = 0 exactly.
- Exact rotating-vs-inertial cross-check (Plummer sphere, constant vector
  Omega): r(t) = R(t)^T x(t) to 7.2e-12 and M_rot(t) = T(t) M_in(t) T(0)^-1
  with T = [[R^T,0],[-[Omega]_x R^T,R^T]], R(t) = expm([Omega]_x t), to
  2.4e-11 (tolerance 1e-9) -- validating all signs/factors of both Jacobian
  blocks at integrator precision, far beyond the 1e-4 FD-of-flow check.
- Flag/gate test: hasC_dxdv3d True + CompositePotential aggregation; the
  pure-Python integrate_dxdv methods raise; a forced hasC_dxdv3d=False falls
  back to odeint with a warning and then raises (never silently wrong).

Regression: pytest tests/test_orbit.py -k "dxdv or liouville" passes 130
(= 126 before + 4 new); tests/test_noninertial.py 53 passed unchanged. The
numpy paths are untouched: the C force and the pure-Python force are
unchanged (the new C Jacobian is only called by the dxdv path) and the only
Python changes are the hasC_dxdv3d flag, docstrings, and error messages.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@jobovy jobovy force-pushed the feat/pvar-tail-noninertial branch from 8b4bc77 to 5849419 Compare June 11, 2026 02:52
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