Skip to content

Instruments

Telescope

Bases: Instrument

Class that represents a telescope instrument, holding an optical system, a source object and (optionally) a detector object, automating the process of modelling all three in conjunction.

To generate more complex instruments or a set of observations, the Telescope class can be inherited and modified to suit the needs of the user.

Attributes:

Name Type Description
optics BaseOpticalSystem

An OpticalSystem object that defines the optical transformations of the instrument.

source BaseSource

A Source or Scene to objects to model through the instrument.

detector BaseDetector

A Detector object that defines the detector transformations of the instrument.

Source code in src/dLux/instruments.py
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
class Telescope(Instrument):
    """
    Class that represents a telescope instrument, holding an optical system, a source
    object and (optionally) a detector object, automating the process of modelling
    all three in conjunction.

    To generate more complex instruments or a set of observations, the `Telescope`
    class can be inherited and modified to suit the needs of the user.

    Attributes
    ----------
    optics : OpticalSystem
        An `OpticalSystem` object that defines the optical transformations of the
        instrument.
    source : Source
        A `Source` or `Scene` to objects to model through the instrument.
    detector : Detector
        A `Detector` object that defines the detector transformations of the
        instrument.
    """

    optics: OpticalSystem
    source: Source
    detector: Detector

    def __init__(
        self: Telescope,
        optics: OpticalSystem,
        source: Union[list, Source],
        detector: Detector = None,
    ):
        """
        Parameters
        ----------
        optics : OpticalSystem
            An `OpticalSystem` object that defines the optical transformations of the
            instrument.
        source : Source
            A `Source` or `Scene` to objects to model through the instrument. Can be
            either a single `Source` object, or a list of `Source` objects which is
            then converted to a `Scene` object. The list entries can also be a tuple of
            (key, source)  in order to specify a key for the source in the scene.
        detector : Detector = None
            A `Detector` object that defines the detector transformations of the
            instrument.
        """
        # Optics
        if not isinstance(optics, OpticalSystem):
            raise TypeError("optics must be an Optics object.")
        self.optics = optics

        # Sources
        if isinstance(source, Source):
            self.source = source
        elif isinstance(source, tuple):
            # If its a (key, source) tuple, we ignore the key
            self.source = source[1]
        else:
            self.source = Scene(source)

        # Detector
        if not isinstance(detector, Detector) and detector is not None:
            raise TypeError(
                "detector must be an Detector object. "
                f"Got type {type(detector)}"
            )
        self.detector = detector

    def __getattr__(self: Telescope, key: str) -> object:
        """
        Raises the attributes from the optics, source and detector to the top level of
        the class.

        Parameters
        ----------
        key : str
            The key of the item to be searched for.

        Returns
        -------
        item : object
            The item corresponding to the supplied key in the sub-dictionaries.
        """
        for attribute in self.__dict__.values():
            if hasattr(attribute, key):
                return getattr(attribute, key)
        raise AttributeError(
            f"{self.__class__.__name__} has no attribute " f"{key}."
        )

    def model(self: Telescope, return_psf: bool = False) -> Array:
        """
        Models the source objects through the optical system and detector.

        Parameters
        ----------
        return_psf : bool = False
            Should the PSF object be returned instead of the psf Array?

        Returns
        -------
        object : Array, PSF
            if `return_psf` is False, the psf Array is returned.
            If `return_psf` is True, the PSF object is returned.

        """
        # Model optics: return_psf=True for more efficient source calculations
        psfs = self.optics.model(self.source, return_psf=True)

        # Array based output
        psf = psfs.data.sum(tuple(range(psfs.ndim)))
        pixel_scale = psfs.pixel_scale.mean()

        # Pass through detector transformations if it exists
        psf_obj = PSF(psf, pixel_scale)
        if self.detector is not None:
            return self.detector.model(psf_obj, return_psf=return_psf)

        # Return psf
        if return_psf:
            return psf_obj
        return psf_obj.data

__getattr__(key)

Raises the attributes from the optics, source and detector to the top level of the class.

Parameters:

Name Type Description Default
key str

The key of the item to be searched for.

required

Returns:

Name Type Description
item object

The item corresponding to the supplied key in the sub-dictionaries.

Source code in src/dLux/instruments.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def __getattr__(self: Telescope, key: str) -> object:
    """
    Raises the attributes from the optics, source and detector to the top level of
    the class.

    Parameters
    ----------
    key : str
        The key of the item to be searched for.

    Returns
    -------
    item : object
        The item corresponding to the supplied key in the sub-dictionaries.
    """
    for attribute in self.__dict__.values():
        if hasattr(attribute, key):
            return getattr(attribute, key)
    raise AttributeError(
        f"{self.__class__.__name__} has no attribute " f"{key}."
    )

__init__(optics, source, detector=None)

Parameters:

Name Type Description Default
optics BaseOpticalSystem

An OpticalSystem object that defines the optical transformations of the instrument.

required
source BaseSource

A Source or Scene to objects to model through the instrument. Can be either a single Source object, or a list of Source objects which is then converted to a Scene object. The list entries can also be a tuple of (key, source) in order to specify a key for the source in the scene.

required
detector Detector = None

A Detector object that defines the detector transformations of the instrument.

None
Source code in src/dLux/instruments.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def __init__(
    self: Telescope,
    optics: OpticalSystem,
    source: Union[list, Source],
    detector: Detector = None,
):
    """
    Parameters
    ----------
    optics : OpticalSystem
        An `OpticalSystem` object that defines the optical transformations of the
        instrument.
    source : Source
        A `Source` or `Scene` to objects to model through the instrument. Can be
        either a single `Source` object, or a list of `Source` objects which is
        then converted to a `Scene` object. The list entries can also be a tuple of
        (key, source)  in order to specify a key for the source in the scene.
    detector : Detector = None
        A `Detector` object that defines the detector transformations of the
        instrument.
    """
    # Optics
    if not isinstance(optics, OpticalSystem):
        raise TypeError("optics must be an Optics object.")
    self.optics = optics

    # Sources
    if isinstance(source, Source):
        self.source = source
    elif isinstance(source, tuple):
        # If its a (key, source) tuple, we ignore the key
        self.source = source[1]
    else:
        self.source = Scene(source)

    # Detector
    if not isinstance(detector, Detector) and detector is not None:
        raise TypeError(
            "detector must be an Detector object. "
            f"Got type {type(detector)}"
        )
    self.detector = detector

model(return_psf=False)

Models the source objects through the optical system and detector.

Parameters:

Name Type Description Default
return_psf bool = False

Should the PSF object be returned instead of the psf Array?

False

Returns:

Name Type Description
object (Array, PSF)

if return_psf is False, the psf Array is returned. If return_psf is True, the PSF object is returned.

Source code in src/dLux/instruments.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def model(self: Telescope, return_psf: bool = False) -> Array:
    """
    Models the source objects through the optical system and detector.

    Parameters
    ----------
    return_psf : bool = False
        Should the PSF object be returned instead of the psf Array?

    Returns
    -------
    object : Array, PSF
        if `return_psf` is False, the psf Array is returned.
        If `return_psf` is True, the PSF object is returned.

    """
    # Model optics: return_psf=True for more efficient source calculations
    psfs = self.optics.model(self.source, return_psf=True)

    # Array based output
    psf = psfs.data.sum(tuple(range(psfs.ndim)))
    pixel_scale = psfs.pixel_scale.mean()

    # Pass through detector transformations if it exists
    psf_obj = PSF(psf, pixel_scale)
    if self.detector is not None:
        return self.detector.model(psf_obj, return_psf=return_psf)

    # Return psf
    if return_psf:
        return psf_obj
    return psf_obj.data

Dither

Bases: Telescope

Simple extension of the Telescope class that applies a series of dithers to the source positions before modelling the instrument. Serves both as a demonstration of how to extend the Telescope class and as a useful tool for modelling dithered observations.

Attributes:

Name Type Description
optics BaseOpticalSystem

An OpticalSystem object that defines the optical transformations of the instrument.

source BaseSource

A Source or Scene to objects to model through the instrument.

detector BaseDetector

A Detector object that defines the detector transformations of the instrument.

dithers (Array, radians)

The array of dithers to apply to the source positions. The shape of the array should be (ndithers, 2).

Source code in src/dLux/instruments.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
class Dither(Telescope):
    """
    Simple extension of the `Telescope` class that applies a series of dithers to the
    source positions before modelling the instrument. Serves both as a demonstration
    of how to extend the `Telescope` class and as a useful tool for modelling
    dithered observations.

    Attributes
    ----------
    optics : OpticalSystem
        An `OpticalSystem` object that defines the optical transformations of the
        instrument.
    source : Source
        A `Source` or `Scene` to objects to model through the instrument.
    detector : Detector
        A `Detector` object that defines the detector transformations of the
        instrument.
    dithers : Array, radians
        The array of dithers to apply to the source positions. The shape of the
        array should be (ndithers, 2).
    """

    dithers: Array

    def __init__(
        self: Telescope,
        dithers: Array,
        optics: OpticalSystem,
        source: Union[list, Source],
        detector: Detector = None,
    ):
        """
        Parameters
        ----------
        dithers : Array, radians
            The array of dithers to apply to the source positions. The shape of the
            array should be (ndithers, 2).
        optics : OpticalSystem
            An `OpticalSystem` object that defines the optical transformations of the
            instrument.
        source : Source
            A `Source` or `Scene` to objects to model through the instrument. Can be
            either a single `Source` object, or a list of `Source` objects which is
            then converted to a `Scene` object. The list entries can also be a tuple of
            (key, source)  in order to specify a key for the source in the scene.
        detector : Detector = None
            A `Detector` object that defines the detector transformations of the
            instrument.
        """
        self.dithers = np.asarray(dithers, float)
        if self.dithers.ndim != 2 or self.dithers.shape[1] != 2:
            raise ValueError("dithers must be an array of shape (ndithers, 2)")
        super().__init__(optics=optics, source=source, detector=detector)

    def model(self: Telescope, return_psf: bool = False) -> Array:
        """
        Models the source objects through the optical system and detector, while also
        applying the dithers to the source positions.

        Parameters
        ----------
        return_psf : bool = False
            Should the PSF object be returned instead of the psf Array?

        Returns
        -------
        object : Array, PSF
            if `return_psf` is False, the psf Array is returned.
            If `return_psf` is True, the PSF object is returned.
        """

        def dither_and_model(dither, instrument):
            instrument = instrument.add("source.position", dither)
            return super(type(instrument), instrument).model(return_psf)

        return vmap(dither_and_model, (0, None))(self.dithers, self)

__init__(dithers, optics, source, detector=None)

Parameters:

Name Type Description Default
dithers (Array, radians)

The array of dithers to apply to the source positions. The shape of the array should be (ndithers, 2).

required
optics BaseOpticalSystem

An OpticalSystem object that defines the optical transformations of the instrument.

required
source BaseSource

A Source or Scene to objects to model through the instrument. Can be either a single Source object, or a list of Source objects which is then converted to a Scene object. The list entries can also be a tuple of (key, source) in order to specify a key for the source in the scene.

required
detector Detector = None

A Detector object that defines the detector transformations of the instrument.

None
Source code in src/dLux/instruments.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def __init__(
    self: Telescope,
    dithers: Array,
    optics: OpticalSystem,
    source: Union[list, Source],
    detector: Detector = None,
):
    """
    Parameters
    ----------
    dithers : Array, radians
        The array of dithers to apply to the source positions. The shape of the
        array should be (ndithers, 2).
    optics : OpticalSystem
        An `OpticalSystem` object that defines the optical transformations of the
        instrument.
    source : Source
        A `Source` or `Scene` to objects to model through the instrument. Can be
        either a single `Source` object, or a list of `Source` objects which is
        then converted to a `Scene` object. The list entries can also be a tuple of
        (key, source)  in order to specify a key for the source in the scene.
    detector : Detector = None
        A `Detector` object that defines the detector transformations of the
        instrument.
    """
    self.dithers = np.asarray(dithers, float)
    if self.dithers.ndim != 2 or self.dithers.shape[1] != 2:
        raise ValueError("dithers must be an array of shape (ndithers, 2)")
    super().__init__(optics=optics, source=source, detector=detector)

model(return_psf=False)

Models the source objects through the optical system and detector, while also applying the dithers to the source positions.

Parameters:

Name Type Description Default
return_psf bool = False

Should the PSF object be returned instead of the psf Array?

False

Returns:

Name Type Description
object (Array, PSF)

if return_psf is False, the psf Array is returned. If return_psf is True, the PSF object is returned.

Source code in src/dLux/instruments.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def model(self: Telescope, return_psf: bool = False) -> Array:
    """
    Models the source objects through the optical system and detector, while also
    applying the dithers to the source positions.

    Parameters
    ----------
    return_psf : bool = False
        Should the PSF object be returned instead of the psf Array?

    Returns
    -------
    object : Array, PSF
        if `return_psf` is False, the psf Array is returned.
        If `return_psf` is True, the PSF object is returned.
    """

    def dither_and_model(dither, instrument):
        instrument = instrument.add("source.position", dither)
        return super(type(instrument), instrument).model(return_psf)

    return vmap(dither_and_model, (0, None))(self.dithers, self)