Skip to content

Spectral FITS WCS - #316

Open
hpparvi wants to merge 10 commits into
astropy:mainfrom
hpparvi:fits_wcs
Open

Spectral FITS WCS#316
hpparvi wants to merge 10 commits into
astropy:mainfrom
hpparvi:fits_wcs

Conversation

@hpparvi

@hpparvi hpparvi commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

This PR adds:

  • The WavelengthSolution1D.wcs method to calculate a spectral FITS WCS by fitting the grism dispersion function (Greisen et al. 2006) to the native polynomial solution. The WCS is returned as an astropy.wcs.WCS object that can be easily serialised to a FITS header.
  • The WavelengthSolution1D.attach_wcs method to attach a (cached) FITS WCS to a Spectrum object, after which the spectrum can be saved/read using the "wcs1d-fits" format.

AI/LLM disclaimer: I used Claude Opus 5 and Claude Fable quite extensively during development (including in agentic mode). That said, I am well familiar with the code and can explain what it does and why.

hpparvi and others added 7 commits August 12, 2026 18:35
…ce pixel keywords (CRPIX, CRVAL, CDELT) exactly. Updated tests and removed global optimization step.
…ation, docstrings, tests, and documentation for consistency with FITS WCS Paper III.
…ed FITS WCS to a Spectrum object. This creates a copy of the spectrum object out of necessity.
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.46835% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.43%. Comparing base (0d2ce22) to head (89ca6f7).

Files with missing lines Patch % Lines
specreduce/wavesol1d.py 97.36% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #316      +/-   ##
==========================================
+ Coverage   92.25%   92.43%   +0.17%     
==========================================
  Files          18       18              
  Lines        2234     2312      +78     
==========================================
+ Hits         2061     2137      +76     
- Misses        173      175       +2     

☔ 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.

@tepickering

Copy link
Copy Markdown
Contributor

i suspect the RTD build failure here is the same as with the other two recent PRs...

@hpparvi

hpparvi commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

The two PRs had slightly different issues in their docs, but both should be fixed now.

Also, I agree with dropping support for Python 3.11. I've now made Python 2.12 as the minium in these two PRs, and marked this to the changelog for specreduce 1.10 (the next release).

header = ws.wcs().to_header()
print(header)

* **Export a GWCS Object**: Access the `~gwcs.wcs.WCS` object representing the polynomial

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might be useful to note here that GWCS maintains the full fidelity of the wavelength solution vs using an approximation to work around the constraints of the FITS standard.

Comment thread specreduce/wavecal1d.py
Boolean indicating whether the input wavelengths correspond to air rather than vacuum;
by default `False`, meaning vacuum wavelengths.
by default `False`, meaning vacuum wavelengths. The flag is also stored in the
wavelength solution, where it selects between the 'AWAV-GRA' and 'WAVE-GRI' axis

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

someone looking at this who is not familiar with Paper III (like me until i went back and read it) would think GRA refers to grating and GRI refers to grism. even the paper itself makes it confusing. should make it clear here or in other documentation that the FITS standard uses GRA to refer to a disperser working in air wavelengths, and GRI to one working in vacuum.

Comment thread specreduce/wavesol1d.py
return models.Polynomial1D(m.degree - 1, **coeffs)


def _make_grism_wcs(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i would personally say disperser instead of grism, but it does make sense to follow the terminology and conventions of the paper.

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.

2 participants