From fbbb151c1cf75e6a69ef0426bfe9700ba760ae67 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sat, 13 Apr 2024 16:01:36 -0600 Subject: [PATCH 1/9] Added `ctis.instruments.Instrument` class. --- ctis/__init__.py | 2 + ctis/instruments/__init__.py | 9 ++++ ctis/instruments/_instruments.py | 75 +++++++++++++++++++++++++++ ctis/instruments/_instruments_test.py | 30 +++++++++++ 4 files changed, 116 insertions(+) create mode 100644 ctis/instruments/__init__.py create mode 100644 ctis/instruments/_instruments.py create mode 100644 ctis/instruments/_instruments_test.py diff --git a/ctis/__init__.py b/ctis/__init__.py index 8107466..fe5117f 100644 --- a/ctis/__init__.py +++ b/ctis/__init__.py @@ -4,7 +4,9 @@ """ from . import scenes +from . import instruments __all__ = [ "scenes", + "instruments", ] diff --git a/ctis/instruments/__init__.py b/ctis/instruments/__init__.py new file mode 100644 index 0000000..6995870 --- /dev/null +++ b/ctis/instruments/__init__.py @@ -0,0 +1,9 @@ +""" +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..6df925d --- /dev/null +++ b/ctis/instruments/_instruments.py @@ -0,0 +1,75 @@ +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 spectral/spatial points on the skyplane to positions on the detector) + and a deprojection model + (which maps positions on the detector to spectral/spatial points on the skyplane). + """ + + @property + @abc.abstractmethod + def project( + self, + ) -> ProjectionCallable: + """ + The forward model of the CTIS instrument. + Maps spectral and spatial coordinates on the field to coordinates + on the detector. + """ + + @property + @abc.abstractmethod + def deproject( + self, + ) -> ProjectionCallable: + """ + The deprojection model of the CTIS instrument. + Maps spectral and spatial coordinates on the detector to coordinates + on the field. + """ + + +@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..f35717d --- /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( + abc.ABC, +): + pass From 2ef28cf2d90efe3e93bca9800bd5d89fb91afaa2 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sat, 13 Apr 2024 16:10:50 -0600 Subject: [PATCH 2/9] Fixed test discovery. --- ctis/instruments/_instruments_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctis/instruments/_instruments_test.py b/ctis/instruments/_instruments_test.py index f35717d..7c6480d 100644 --- a/ctis/instruments/_instruments_test.py +++ b/ctis/instruments/_instruments_test.py @@ -22,9 +22,9 @@ def test_deproject(self, a: ctis.instruments.AbstractInstrument): project=lambda x: x, deproject=lambda x: x, ) - ] + ], ) class TestInstrument( - abc.ABC, + AbstractTestAbstractInstrument, ): pass From 12b78083a9a0a7f457e68d474a6a60b69fd2f643 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sat, 13 Apr 2024 16:18:35 -0600 Subject: [PATCH 3/9] Fixing black errors. --- ctis/instruments/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctis/instruments/__init__.py b/ctis/instruments/__init__.py index 6995870..cd2312f 100644 --- a/ctis/instruments/__init__.py +++ b/ctis/instruments/__init__.py @@ -1,9 +1,10 @@ """ Models of CTIS instruments used during inversions. """ + from ._instruments import AbstractInstrument, Instrument __all__ = [ "AbstractInstrument", - "Instrument" + "Instrument", ] From 1b02aa35361db520e9b3460dbb9455e2fcfa8224 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Wed, 13 Nov 2024 12:18:58 -0700 Subject: [PATCH 4/9] old changes --- ctis/instruments/_instruments.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index 6df925d..7b059ad 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -23,31 +23,41 @@ class AbstractInstrument( An interface describing a CTIS instrument. This consists of a forward model - (which maps spectral/spatial points on the skyplane to positions on the detector) + (which maps the spectral radiance of a physical scene to counts on a detector) and a deprojection model - (which maps positions on the detector to spectral/spatial points on the skyplane). + (which maps detector counts to the spectral radiance of a physical scene). """ - @property @abc.abstractmethod def project( self, - ) -> ProjectionCallable: + 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. """ - @property @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. """ From 7b96972644e322e887c8c2327686205c2744f686 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 21 Apr 2025 18:06:41 -0600 Subject: [PATCH 5/9] Added ideal instrument --- ctis/instruments/_instruments.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index 7b059ad..9164596 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -1,6 +1,7 @@ -from typing import Callable +from typing import Callable, Sequence import abc import dataclasses +import astropy.units as u import named_arrays as na __all__ = [ @@ -61,6 +62,22 @@ def deproject( """ +@dataclasses.dataclass +class IdealInstrument( + AbstractInstrument, +): + """ + An idealized CTIS instrument which has a perfect point-spread function + and no noise. + """ + + dispersion: u.Quantity | na.AbstractScalar + r"""The magnitude of the dispersion in :math:`\text{m \AA} \,\text{pix}^-1`""" + + angle: u.Quantity | na.AbstractScalar + """The angle of the dispersion direction with respect to the scene.""" + + @dataclasses.dataclass class Instrument( AbstractInstrument, From aacadd28d511a9966c720324aff505dea2ec13bb Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 21 Apr 2025 18:12:57 -0600 Subject: [PATCH 6/9] Make `IdealInstrument` public --- ctis/instruments/_instruments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index 9164596..652481c 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -6,6 +6,7 @@ __all__ = [ "AbstractInstrument", + "IdealInstrument", "Instrument", ] From d1603b7d8734e5703ee18e837357e426f6c27e19 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Tue, 22 Apr 2025 11:11:26 -0600 Subject: [PATCH 7/9] intermediate commit --- ctis/instruments/_instruments.py | 56 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index 652481c..d42b854 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -7,7 +7,6 @@ __all__ = [ "AbstractInstrument", "IdealInstrument", - "Instrument", ] @@ -22,7 +21,7 @@ class AbstractInstrument( abc.ABC, ): """ - An interface describing a CTIS instrument. + An interface describing a general CTIS instrument. This consists of a forward model (which maps the spectral radiance of a physical scene to counts on a detector) @@ -47,9 +46,9 @@ def project( """ @abc.abstractmethod - def deproject( + def backproject( self, - projections: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], + images: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], ) -> ProjectionCallable: """ The deprojection model of the CTIS instrument. @@ -58,46 +57,47 @@ def deproject( Parameters ---------- - projections - The counts gathered by each detector in the CTIS instrument. + images + The number of electrons gathered by each pixel in every channel. """ @dataclasses.dataclass -class IdealInstrument( +class AbstractLinearInstrument( AbstractInstrument, ): """ - An idealized CTIS instrument which has a perfect point-spread function - and no noise. + An instrument that can be modeled using matrix multiplication. """ - dispersion: u.Quantity | na.AbstractScalar - r"""The magnitude of the dispersion in :math:`\text{m \AA} \,\text{pix}^-1`""" + @property + @abc.abstractmethod + def _weights(self) -> tuple[na.AbstractScalar, dict[str, int], dict[str, int]]: + """ + A sparse matrix which maps spectral radiance on the skyplane to + the number of electrons measured by the sensor. + """ + + def project( + self, + scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], + ) -> na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar]: + + pass - angle: u.Quantity | na.AbstractScalar - """The angle of the dispersion direction with respect to the scene.""" @dataclasses.dataclass -class Instrument( +class IdealInstrument( AbstractInstrument, ): """ - A CTIS instrument where the forward and deprojection models are explicitly - provided. + An idealized CTIS instrument which has a perfect point-spread function + and no noise. """ - project: ProjectionCallable = dataclasses.MISSING - """ - The forward model of the CTIS instrument. - Maps spectral and spatial coordinates on the field to coordinates - on the detector. - """ + dispersion: u.Quantity | na.AbstractScalar + r"""The magnitude of the dispersion in :math:`\text{m \AA} \,\text{pix}^-1`""" - deproject: ProjectionCallable = dataclasses.MISSING - """ - The deprojection model of the CTIS instrument. - Maps spectral and spatial coordinates on the detector to coordinates - on the field. - """ + angle: u.Quantity | na.AbstractScalar + """The angle of the dispersion direction with respect to the scene.""" From ea8bd956b0254b98c893b2eb9eb691e519558e1b Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Tue, 22 Apr 2025 18:51:07 -0600 Subject: [PATCH 8/9] messing with the interface --- ctis/instruments/_instruments.py | 41 +++++++++++++++++--------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index d42b854..95f548d 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -23,6 +23,9 @@ class AbstractInstrument( """ An interface describing a general CTIS instrument. + The only member of this interface is :meth:`image`, + which represents the forward model of the 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 @@ -30,35 +33,30 @@ class AbstractInstrument( """ @abc.abstractmethod - def project( + def image( 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. - + f""" + The forward model of this CTIS instrument, which maps spectral radiance + on the skyplane to counts on the detectors. + Parameters ---------- scene - The spectral radiance of each spatial/spectral point in the scene. + The spectral radiance in units equivalent to + {(u.erg / (u.cm**2 * u.sr * u.AA * u.s)):latex_inline}. """ + @property @abc.abstractmethod - def backproject( - self, - images: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], - ) -> ProjectionCallable: + def coordinates_scene(self) -> na.AbstractSpectralPositionalVectorArray: """ - The deprojection model of the CTIS instrument. - Maps spectral and spatial coordinates on the detector to coordinates - on the field. + A grid of wavelength and position coordinates on the skyplane + which will be used to construct the inverted scene. - Parameters - ---------- - images - The number of electrons gathered by each pixel in every channel. + Normally the pitch of this grid is chosen to be the average + plate scale of the instrument. """ @@ -78,6 +76,12 @@ def _weights(self) -> tuple[na.AbstractScalar, dict[str, int], dict[str, int]]: the number of electrons measured by the sensor. """ + def image( + self, + scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], + ) -> na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar]: + pass + def project( self, scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar], @@ -86,7 +90,6 @@ def project( pass - @dataclasses.dataclass class IdealInstrument( AbstractInstrument, From 803ede57e4f5321fe926e9d5e5ef5df77c9f99ff Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Tue, 22 Apr 2025 23:12:39 -0600 Subject: [PATCH 9/9] imports --- ctis/instruments/__init__.py | 9 +++++++-- ctis/instruments/_instruments.py | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ctis/instruments/__init__.py b/ctis/instruments/__init__.py index cd2312f..bcc5400 100644 --- a/ctis/instruments/__init__.py +++ b/ctis/instruments/__init__.py @@ -2,9 +2,14 @@ Models of CTIS instruments used during inversions. """ -from ._instruments import AbstractInstrument, Instrument +from ._instruments import ( + AbstractInstrument, + AbstractLinearInstrument, + IdealInstrument, +) __all__ = [ "AbstractInstrument", - "Instrument", + "AbstractLinearInstrument", + "IdealInstrument", ] diff --git a/ctis/instruments/_instruments.py b/ctis/instruments/_instruments.py index 95f548d..92ad102 100644 --- a/ctis/instruments/_instruments.py +++ b/ctis/instruments/_instruments.py @@ -6,6 +6,7 @@ __all__ = [ "AbstractInstrument", + "AbstractLinearInstrument", "IdealInstrument", ]