Add SimpleDistortionModel, an analytic linear distortion#157
Merged
Conversation
Introduce `AbstractLinearDistortionModel`, an affine distortion (`distort = matrix @ (c - center) + intercept`) whose `undistort` is the exact inverse via `matrix.inverse`, and a concrete `SimpleDistortionModel` parameterized by `plate_scale`, `dispersion`, `angle`, `wavelength_ref`, and `position_ref`. It builds the rotation + dispersion + plate-scale `SpectralPositionalMatrixArray`, capturing the distortion of an idealized spectrograph (cf. the ctis `IdealInstrument` distortion). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`SimpleDistortionModel` now takes one `reference: SpectralPositionalVectorArray` (the reference wavelength + the sensor position the field center maps to) instead of separate `wavelength_ref`/`position_ref` fields; `intercept` is then just `reference`. The class example now distorts a grid and scatter-plots the result on the sensor, colored by wavelength. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #157 +/- ##
=======================================
Coverage 99.31% 99.31%
=======================================
Files 114 114
Lines 5671 5722 +51
=======================================
+ Hits 5632 5683 +51
Misses 39 39
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
`na.plt.scatter(c=...)` lets matplotlib autoscale a colormap `Normalize`, which calls `float()` on the color values; a `Quantity` wavelength raises "only dimensionless scalar quantities can be converted to Python scalars" when the figure is rendered (as it is by jupyter-execute, breaking the Read the Docs build). Pass `sensor.wavelength.to_value(u.nm)` so `c` is unitless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Increase the example dispersion to 2 nm/pix so the wavelength channels are less spread out (the previous 0.1 nm/pix dispersed them ~±500 px versus the ~±10 px field), and label the detector x/y axes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scatter one series per wavelength (selected with a `where` mask) so each gets its own color and a legend entry, instead of an unlabeled color mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an analytic (non-fit) distortion model to
optika.distortion, alongside the existing polynomial-fit model.AbstractLinearDistortionModel— an affine distortion,distort(c) = matrix @ (c - center) + intercept, with abstractmatrix/center/intercept. Because the map is linear,undistortis its exact inverse viamatrix.inverse(unlike the approximate inverse fit ofPolynomialDistortionModel).SimpleDistortionModel— a concrete linear model parameterized byplate_scale,dispersion,angle,wavelength_ref, andposition_ref. It builds the rotation + dispersion + plate-scalena.SpectralPositionalMatrixArray, capturing the distortion of an idealized spectrograph.Motivation
ctis.instruments.IdealInstrumentimplements exactly this analytic rotation+dispersion distortion inline. Hosting it in optika (as a concreteAbstractDistortionModel) letsctisreuse it, complementing the data-fitPolynomialDistortionModelfor the raytrace-based interpolated system.Testing
SimpleDistortionModel.distortreproduces theIdealInstrumentformula (~1e-13 pix), carries wavelength through exactly, and round-trips exactly (~1e-13 arcsec).pytest optika/distortion/_distortion_test.py— 42 passed; 100% coverage;black/ruffclean.🤖 Generated with Claude Code