diff --git a/ctis/__init__.py b/ctis/__init__.py index 41d44a5..b8c9e63 100644 --- a/ctis/__init__.py +++ b/ctis/__init__.py @@ -2,3 +2,9 @@ A package for inverting imagery captured by a computed tomography imaging spectrograph. """ + +from . import instruments + +__all__ = [ + "instruments", +] diff --git a/ctis/instruments/__init__.py b/ctis/instruments/__init__.py new file mode 100644 index 0000000..cd2312f --- /dev/null +++ b/ctis/instruments/__init__.py @@ -0,0 +1,10 @@ +""" +Models of CTIS instruments used during inversions. +""" + +from ._instruments import AbstractInstrument, Instrument + +__all__ = [ + "AbstractInstrument", + "Instrument", +] diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py new file mode 100644 index 0000000..7b059ad --- /dev/null +++ b/ctis/instruments/_instruments.py @@ -0,0 +1,85 @@ +from typing import Callable +import abc +import dataclasses +import named_arrays as na + +__all__ = [ + "AbstractInstrument", + "Instrument", +] + + +ProjectionCallable = Callable[ + [na.FunctionArray[na.SpectralPositionalVectorArray, na.ScalarArray]], + na.FunctionArray[na.SpectralPositionalVectorArray, na.ScalarArray], +] + + +@dataclasses.dataclass +class AbstractInstrument( + abc.ABC, +): + """ + An interface describing a CTIS instrument. + + This consists of a forward model + (which maps the spectral radiance of a physical scene to counts on a detector) + and a deprojection model + (which maps detector counts to the spectral radiance of a physical scene). + """ + + @abc.abstractmethod + def project( + self, + scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], + ) -> na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar]: + """ + The forward model of the CTIS instrument. + Maps spectral and spatial coordinates on the field to coordinates + on the detector. + + Parameters + ---------- + scene + The spectral radiance of each spatial/spectral point in the scene. + """ + + @abc.abstractmethod + def deproject( + self, + projections: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], + ) -> ProjectionCallable: + """ + The deprojection model of the CTIS instrument. + Maps spectral and spatial coordinates on the detector to coordinates + on the field. + + Parameters + ---------- + projections + The counts gathered by each detector in the CTIS instrument. + """ + + +@dataclasses.dataclass +class Instrument( + AbstractInstrument, +): + """ + A CTIS instrument where the forward and deprojection models are explicitly + provided. + """ + + project: ProjectionCallable = dataclasses.MISSING + """ + The forward model of the CTIS instrument. + Maps spectral and spatial coordinates on the field to coordinates + on the detector. + """ + + deproject: ProjectionCallable = dataclasses.MISSING + """ + The deprojection model of the CTIS instrument. + Maps spectral and spatial coordinates on the detector to coordinates + on the field. + """ diff --git a/ctis/instruments/_instruments_test.py b/ctis/instruments/_instruments_test.py new file mode 100644 index 0000000..7c6480d --- /dev/null +++ b/ctis/instruments/_instruments_test.py @@ -0,0 +1,30 @@ +import pytest +import abc +import ctis + + +class AbstractTestAbstractInstrument( + abc.ABC, +): + def test_project(self, a: ctis.instruments.AbstractInstrument): + result = a.project + assert hasattr(result, "__call__") + + def test_deproject(self, a: ctis.instruments.AbstractInstrument): + result = a.deproject + assert hasattr(result, "__call__") + + +@pytest.mark.parametrize( + argnames="a", + argvalues=[ + ctis.instruments.Instrument( + project=lambda x: x, + deproject=lambda x: x, + ) + ], +) +class TestInstrument( + AbstractTestAbstractInstrument, +): + pass