Skip to content

Fix polchisq psi parametrization and gmst_to_utc inverse#251

Merged
achael merged 3 commits into
dev-backendfrom
fix/obsdata-polchisq-gmst
May 18, 2026
Merged

Fix polchisq psi parametrization and gmst_to_utc inverse#251
achael merged 3 commits into
dev-backendfrom
fix/obsdata-polchisq-gmst

Conversation

@achael
Copy link
Copy Markdown
Owner

@achael achael commented May 15, 2026

Summary

Two latent bugs in obsdata.py + obs_helpers.py, plus an explicit warning for the multi-day aliasing limit. The two xfail(strict=True) regression tests landed via #249/#250 are now passing and have had their decorators dropped.

Bug 1 — Obsdata.polchisq psi parametrization

obsdata.py:1188 packed psi = arcsin(V/I), but pol_imager_utils.make_v_image defines V = I*rho*sin(psi). The imager and the chisq disagreed on what psi meant, producing an O(1e-2) self-consistency residue when evaluated on the generating image. Fixed to psi = arcsin(V/(I*rho)).

Bug 2 — obs_helpers.gmst_to_utc inverse

obs_helpers.py:976 used a hardcoded constant solar/sidereal ratio (0.99726956...) for a linear inverse, while utc_to_gmst called astropy per-sample. The mismatched rate accumulated to several minutes over a 24h obs.

Replaced with astropy-derived local sidereal rate (sample at mjd and mjd+1), plus a [0, 24) wrap on the sidereal delta so UTC lands in the canonical solar day of mjd. Roundtrip is now ~1e-11 hr for any obs that fits in one sidereal day (~23.93 solar hours).

Companion warning — multi-day aliasing

The >23.93h aliasing limit is fundamental: GMST mod 24 is many-to-one in solar UTC, and once data is in GMST the diagnostic signal is gone. Added a UserWarning in Obsdata.switch_timetype on the UTC->GMST branch only — the only direction where the unaliased span is still visible in the input. The inverse GMST->UTC has no signal to detect this, so no warning there.

Test plan

  • pytest tests/test_obsdata.py → 169 passed, 1 skipped
  • pytest tests/ (full suite, on prior unrebased commit) → 730 passed, 91 skipped
  • The two previously-xfail(strict=True) tests (test_switch_timetype_roundtrip_utc_gmst, test_polchisq_self_consistency) now pass; their decorators are removed.
  • Three new tests covering the multi-day warning (test_switch_timetype_warns_on_multi_day_to_gmst, test_switch_timetype_no_warn_on_single_day, test_switch_timetype_no_warn_on_gmst_to_utc).
  • Multi-day stress: verified utc_to_gmst handles negative / >24 / multi-day inputs; gmst_to_utc returns UTC in [0, 24) of mjd with <1e-9 hr roundtrip for any single-sidereal-day obs.

Two latent bugs in obsdata.py + obs_helpers.py, plus an explicit
warning for the multi-day aliasing limitation:

* Obsdata.polchisq packed psi = arcsin(V/I), but
  pol_imager_utils.make_v_image defines V = I*rho*sin(psi). The
  imager and the chisq disagreed on what psi meant, producing an
  O(1e-2) self-consistency residue when the chisq was evaluated
  on the generating image. Pack psi = arcsin(V/(I*rho)).

* obs_helpers.gmst_to_utc used a hardcoded constant solar/sidereal
  ratio (0.99726956...) for a linear inverse, while utc_to_gmst
  called astropy per-sample. The mismatched rate accumulated to
  several minutes over a 24h obs. Replace with astropy-derived
  local sidereal rate (sample at mjd and mjd+1) plus a [0, 24) wrap
  on the sidereal delta so utc lands in the canonical solar day of
  mjd. Roundtrip is now 1e-11 hr for any obs that fits in one
  sidereal day (~23.93 solar hours).

* The >23.93h aliasing limit is pre-existing and fundamental
  (sidereal time mod 24 is many-to-one in solar UTC). Add a
  UserWarning in Obsdata.switch_timetype on the UTC->GMST branch
  -- the only direction where the unaliased span is still visible
  in the input. The inverse GMST->UTC has no signal to detect this
  (GMST is already wrapped to [0, 24)), so no warning there.

Regression tests added to tests/test_obsdata.py.
@achael achael marked this pull request as ready for review May 15, 2026 17:51
@achael achael requested a review from rohandahale May 15, 2026 17:52
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 26.87%. Comparing base (e852f37) to head (7ba4c0f).

Additional details and impacted files
@@               Coverage Diff               @@
##           dev-backend     #251      +/-   ##
===============================================
+ Coverage        26.85%   26.87%   +0.02%     
===============================================
  Files               50       50              
  Lines            25321    25328       +7     
  Branches          4261     4262       +1     
===============================================
+ Hits              6800     6807       +7     
  Misses           17821    17821              
  Partials           700      700              

☔ View full report in Codecov by Sentry.
📢 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.

rohandahale
rohandahale previously approved these changes May 15, 2026
Copy link
Copy Markdown
Collaborator

@rohandahale rohandahale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@achael Looks good!

One trivial CI fix: lint is red on tests/test_obsdata.py:11:1: I001 Import block is un-sorted. The new import warnings needs to go above import numpy as np. ruff check --fix tests/test_obsdata.py closes it.

Two minor suggestions:

  1. psivec = np.arcsin(...) is exact mathematically (V/(I·rho) ∈ [-1, 1] by construction) but a defensive np.arcsin(np.clip(..., -1, 1)) guards against floating-point overshoot at the boundary.
  2. At fully unpolarized pixels (rho = 0), the fixed expression hits 0/0 → NaN. The old code dodged it by computing arcsin(0) = 0.

psivec = np.where(rhovec > 0, np.arcsin(np.clip(imstokes.vvec / (ivec * rhovec), -1, 1)), 0.0).
Returns the old arcsin(0) = 0

@achael
Copy link
Copy Markdown
Owner Author

achael commented May 18, 2026

updated following your review suggestions @rohandahale , merging now

@achael achael merged commit 1550d20 into dev-backend May 18, 2026
5 checks passed
@rohandahale rohandahale added this to the 2.0 milestone May 18, 2026
@rohandahale rohandahale deleted the fix/obsdata-polchisq-gmst branch May 19, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants