Electron-based instrument interface and OptikaInstrument - #18
Draft
roytsmart wants to merge 7 commits into
Draft
Conversation
LinearOptikaInstrumentOptikaInstrument
roytsmart
force-pushed
the
feature/electron-based-instruments
branch
from
July 25, 2026 20:23
cb78062 to
dc1097c
Compare
Make the CTIS instrument interface electron-based: `image()` returns the electrons measured by the sensor and `backproject()` accepts electrons. `IdealInstrument` gains a `quantum_yield` parameter and converts photons<->electrons around its regridding model. `LinearOptikaInstrument` wraps an `optika.systems.LinearSystem` and delegates to its `image_from_weights`/`backproject_from_weights`, reusing the cached regridding weights so the forward/transpose model (including the sensor's `expose`/`photons_absorbed`) is not rebuilt each call. `image()` gains an `uncertainty` flag: when set, it attaches the standard deviation of the measurement noise as a `NormalUncertainScalarArray`. The variance is computed per wavelength and summed in quadrature during the wavelength integration, so it is exact even for the integrated image. The MART inverter uses this to weight its chi-squared, replacing the old per-instrument `uncertainty` callable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename `LinearOptikaInstrument` to `OptikaInstrument` ("Linear" is already
implied by `AbstractLinearInstrument`; the distinguishing trait is that it
is backed by an `optika` system).
Generalize `AbstractTestAbstractInstrument` so the shared tests derive the
scene and coordinates from the instrument under test, and add
`TestOptikaInstrument` with a channel-aware `LinearSystem` fixture whose
scene grid spans the field of view. A new `test_backproject_conserves_flux`
asserts that re-imaging a backprojection preserves the total measured
electrons (the forward model's adjoint property) for every instrument.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delegate the wavelength integration to optika: `OptikaInstrument` now forwards `integrate` to `system.image_from_weights`/`backproject_from_weights` (which sum over wavelength and apply the sensor's read noise once), instead of integrating in ctis. This fixes the sqrt(N_wavelength) over-count that resulted from the sensor applying read noise per wavelength. `IdealInstrument` gains a `read_noise` field, applied once (Gaussian on the nominal, in quadrature on the uncertainty) after its own wavelength integration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a shared `test_read_noise` (with a per-instrument `_with_read_noise` hook) verifying that a nonzero read noise raises the integrated uncertainty by exactly `read_noise` in quadrature, guarding against the sqrt(N_wavelength) over-count. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`LinearSystem.coordinates_sensor` is now in pixel units, so the test fixture's distortion is expressed in arcsec/pix and nm/pix to match. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
roytsmart
force-pushed
the
feature/electron-based-instruments
branch
from
July 26, 2026 02:34
dc1097c to
a99bd94
Compare
The forward model accepts a scene in photon or energy units, so let
`backproject` return the spectral radiance in whichever the caller requests,
so an inversion can be expressed in the same units as its input scene:
instrument.backproject(image, unit=scene.outputs.unit)
`OptikaInstrument` forwards `unit` to optika's `backproject`, where the
wavelength and sensor physics live. `IdealInstrument`, a standalone analytic
model with no optika system, converts locally: its backprojection is in energy
units and is divided by the energy per photon when a photon unit is requested.
The default (`unit=None`) preserves the previous behavior of each instrument.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ
Replace the hand-written `wavelength.volume_cell(...) * position.volume_cell(...)` (with the manual cell-center alignment) in `AbstractLinearInstrument._volume_scene` with `coordinates.volume_cell(...)` from named-arrays 2.3. The scene coordinates are coerced to a spectral-positional vector first so that `IdealInstrument`'s Doppler scene also gets the method. Bump the named-arrays pin to ~=2.3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CtdKmedevWkDab6BWupXqQ
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.
Makes the CTIS instrument interface electron-based and adds a real forward/inverse model backed by
optika.Interface
image()returns the electrons measured by the sensor;backproject()accepts electrons.uncertaintycallable is gone, replaced by animage(..., uncertainty=True)flag (see below).IdealInstrumentquantum_yieldparameter (electron/photon) and converts photons↔electrons around its regridding model.read_noiseparameter, applied once per readout after the wavelength integration.OptikaInstrument(new)optika.systems.LinearSystemand delegates the forward/transpose — including the wavelength integration and read noise — tosystem.image_from_weights/backproject_from_weights, reusing the cached regridding weights/transpose so the operators aren't rebuilt on every call (important for the iterative MART solve).IdealInstrument(u.pix) convention.Uncertainty
image(..., uncertainty=True)attaches the measurement-noise standard deviation as aNormalUncertainScalarArray.MartInverterweights its χ² with the attached width.Tests
AbstractTestAbstractInstrumentsuite (image, backproject, flux conservation, uncertainty, read-noise-once) runs for bothIdealInstrumentandOptikaInstrument.All
ctistests pass locally (instruments + inverters).🤖 Generated with Claude Code