Skip to content

feat: init binned spectrum and add axis conversions - #61

Open
jamesrafe wants to merge 10 commits into
mainfrom
feat/init-binned-spectrum-and-add-axis-conversion
Open

feat: init binned spectrum and add axis conversions#61
jamesrafe wants to merge 10 commits into
mainfrom
feat/init-binned-spectrum-and-add-axis-conversion

Conversation

@jamesrafe

@jamesrafe jamesrafe commented Aug 7, 2026

Copy link
Copy Markdown

Adds a BinnedSpectrum type and unit-aware spectral axis conversions.

BinnedSpectrum

A new Spectrum kind for binned data (e.g., X-ray spectra): the spectral axis is an $m \times 2$ matrix of bin edges and the flux axis is an $m$-length vector.

spec = spectrum([1.0 2.0; 2.0 3.0; 3.0 4.0]u"keV", [1.0, 2.0, 3.0]u"Jy")
  • Constructed through spectrum() for both unitless and unitful inputs, with validation: exactly two edge columns, and each row strictly ordered in the direction of the spectral axis (catches swapped, mixed-order, and zero-width edges; descending axes are supported).
  • Indexing (spec[i] for any Integer, ranges, masks), iteration, and display work like the other spectrum kinds; redshift/doppler_shift handle bin edges.
  • redden/deredden reject binned spectra with an explicit error (extinction laws need a bin-integration convention) instead of failing inside DustExtinction.

Axis conversions

Unitful.uconvert is extended for spectra, converting between wavelength, frequency, and photon energy via the photon equivalence $E = hν = h c / λ$ (powered by UnitfulEquivalences.jl):

uconvert(u"THz", spec) # Convert the spectral axis, flux carried unchanged
uconvert((u"THz", u"Jy"), spec) # Also convert flux density
uconvert(unit(other_spec), spec) # Match another spectrum's units
  • Same-dimension targets are plain unit conversions, so conversions are idempotent.
  • Conversions are elementwise: a reciprocal conversion (e.g. wavelength → frequency) yields an axis with the opposite direction of monotonicity.
  • Flux density conversion uses the new SpectralDensity(at) equivalence (re-exported here), which preserves integrals: $F_ν = F_λ λ² / c$. It is undefined for binned spectra (bin values are integrated quantities) and errors accordingly.
  • redshift/doppler_shift now also handle frequency and energy axes (compressing instead of stretching).

Dependencies

We are seeing if SpectralDensity can be upstreamed to sostock/UnitfulEquivalences.jl#31.

@fjebaker fjebaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@icweaver
icweaver self-requested a review August 11, 2026 00:29
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.87234% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.80%. Comparing base (e3b22ad) to head (862f9da).

Files with missing lines Patch % Lines
src/transforms/spectral_axis.jl 92.85% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #61      +/-   ##
==========================================
+ Coverage   95.00%   96.80%   +1.80%     
==========================================
  Files           9       11       +2     
  Lines         180      219      +39     
==========================================
+ Hits          171      212      +41     
+ Misses          9        7       -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.

@icweaver

icweaver commented Aug 15, 2026

Copy link
Copy Markdown
Member

This is a really nice PR, thanks @jamesrafe! I think this would be a good opportunity to flex our ecosystem here and start factoring out some common functionality I've been seeing pop-up in a few of our packages.

For starters: @cgarling, what would you think about upstreaming your spectral density conversions in PhotometricFilters.jl to UnitfulEquivalences.jl? I've opened a PR to demonstrate what that might look like sostock/UnitfulEquivalences.jl#31

Next, I think we could replace the custom to_wavelength/to_frequency API introduced in this PR with simple methods extending Untiful.jl's uconvert. Something like this seems ergonomic to me:

uconvert(u"THz", spec) # Just convert the spectral axis
uconvert((u"keV", u"W/m^2/keV"), spec) # Convert the spectral AND flux axis

compared to the separate with_spectral_axis_and_flux_units()/with_spectral_axis_unit()/with_flux_unit() that astropy/specutils seems to juggle

There's also some cleanup in the binned spectrum logic I think we could apply here. This will all be included in my review, but just wanted to give y'all a heads up first on where I was thinking of taking this

@cgarling

Copy link
Copy Markdown
Member

No problem with moving the spectral density conversions if they accept the pr, IMO its a little more specialized than their other equivalences so IDK if they will want to add it

@icweaver

Copy link
Copy Markdown
Member

I may have been spoiled with the DynamicQuantities dev being so receptive to some of our astro-specific PRs in the past, so will see I guess 😅

@icweaver
icweaver marked this pull request as ready for review August 17, 2026 10:40
@icweaver
icweaver removed their request for review August 17, 2026 10:41
@icweaver

Copy link
Copy Markdown
Member

Ok, I think the bones are in place now. Instead of a separate review at this stage, I've just added a PR description to summarize where things are at. It depends on one moving target, the potential PR to UnitfulEquivalences.jl for our unit conversions if they are amenable, but I think the other main piece of this PR, the addition of BinnedSpectrum, is ready for another pass. I just filled in (862f9da) a few of the remaining gaps in the implementation, which are summarized in the above PR description along with James' other really nice additions.

Please have a look all, and feel free to make any additional suggestions if you have any at this time. I know I personally want to polish up the plotting bits, but I think that can be saved for a future PR.

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.

4 participants