Skip to content

Psfs¤

PSF

dLux.psfs.PSF ¤

Bases: Base

A simple class that holds the state of a PSF as it is transformed by detector layers.

UML

UML

Attributes:

Name Type Description
data Array

The PSF as it is transformed by the detector.

pixel_scale Array

The pixel scale of the PSF.

npixels (int, property)

Derived property from data; returns the PSF side length in pixels.

ndim (int, property)

Derived property from pixel_scale; returns PSF vectorisation rank.

Source code in dLux/psfs.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 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
145
146
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
class PSF(zdx.Base):
    """
    A simple class that holds the state of a PSF as it is transformed by detector
    layers.

    ??? abstract "UML"
        ![UML](../../assets/uml/PSF.png)

    Attributes
    ----------
    data : Array
        The PSF as it is transformed by the detector.
    pixel_scale : Array
        The pixel scale of the PSF.
    npixels : int, property
        Derived property from `data`; returns the PSF side length in pixels.
    ndim : int, property
        Derived property from `pixel_scale`; returns PSF vectorisation rank.
    """

    data: Array
    pixel_scale: Array

    def __init__(self: PSF, data: Array, pixel_scale: Array):
        """
        Parameters
        ----------
        data : Array
            The PSF to be transformed by the detector.
        pixel_scale : Array
            The pixel scale of the PSF.
        """
        self.data = np.asarray(data, dtype=float)
        self.pixel_scale = np.asarray(pixel_scale, dtype=float)

    @property
    def npixels(self: PSF) -> int:
        """
        Returns the side length of the arrays currently representing the PSF.

        Returns
        -------
        npixels : int
            The number of pixels that represent the `PSF`.
        """
        return self.data.shape[-1]

    @property
    def ndim(self: PSF) -> int:
        """
        Returns the number of dimensions of the PSF. This is used to track the
        vectorised version of the PSF returned from vmapping.

        Returns
        -------
        ndim : int
            The dimensionality of the PSF.
        """
        return self.pixel_scale.ndim

    def downsample(self: PSF, n: int) -> PSF:
        """
        Downsamples the PSF by a factor of n. This is done by summing the PSF pixels in
        n x n blocks.

        Parameters
        ----------
        n : int
            The factor by which to downsample the PSF.

        Returns
        -------
        psf : PSF
            The downsampled PSF.
        """
        data = dlu.downsample(self.data, n, mean=False)
        pixel_scale = self.pixel_scale * n
        return self.set(data=data, pixel_scale=pixel_scale)

    def convolve(self: PSF, other: Array, method: str = "auto") -> PSF:
        """
        Convolves the PSF with an input array.

        Parameters
        ----------
        other : Array
            The array to convolve with the PSF.
        method : str = "auto"
            The method to use for the convolution. Can be "auto", "direct",
            or "fft". Is "auto" by default, which calls "direct".

        Returns
        -------
        psf : PSF
            The convolved PSF.
        """
        return self.set(data=convolve(self.data, other, mode="same", method=method))

    def rotate(self: PSF, angle: float, method: str = "linear") -> PSF:
        """
        Rotates the PSF by a given angle via interpolation.

        Parameters
        ----------
        angle : float
            The angle by which to rotate the PSF.
        method : str = "linear"
            The interpolation method.

        Returns
        -------
        psf : PSF
            The rotated PSF.
        """
        return self.set(data=dlu.rotate(self.data, angle, method=method))

    def resize(self: PSF, npixels: int) -> PSF:
        """
        Resizes the PSF via a zero-padding or cropping operation.

        Parameters
        ----------
        npixels : int
            The size to resize the PSF to.

        Returns
        -------
        psf : PSF
            The resized PSF.
        """
        return self.set(data=dlu.resize(self.data, npixels))

    def flip(self: PSF, axis: tuple) -> PSF:
        """
        Flips the PSF along the specified axes. Note we use 'ij' indexing, so axis 0 is
        the y-axis and axis 1 is the x-axis.

        Parameters
        ----------
        axis : tuple
            The axes along which to flip the PSF.

        Returns
        -------
        psf : PSF
            The new flipped PSF.
        """
        return self.set(data=np.flip(self.data, axis))

    def _magic_unified_op(self: PSF, other: Array | PSF | None, op: str) -> PSF:
        """
        Internal helper function to unify the logic of the magic methods for addition,
        subtraction, multiplication and division.

        Parameters
        ----------
        other : Array | PSF | None
            The object to operate with. Can be an array, a PSF, or None.
        op : str
            The operation to perform: 'add', 'subtract', 'multiply', or 'divide'.

        Returns
        -------
        psf : PSF
            The resulting PSF after applying the operation.
        """
        # Nones always return unchanged
        if other is None:
            return self

        # Check for supported types
        if not isinstance(other, (PSF, Array, float, int, complex)):
            raise TypeError(
                f"Unsupported type for {op}: {type(other)}. Must be an array, "
                "PSF, or None."
            )

        # Extract data if other is a PSF
        if isinstance(other, PSF):
            other = other.data

        # Apply the operation
        if op == "add":
            return self.add("data", other)
        elif op == "subtract":
            return self.add("data", -other)
        elif op == "multiply":
            return self.multiply("data", other)
        elif op == "divide":
            return self.multiply("data", 1 / other)
        else:
            raise ValueError(f"Unsupported operation '{op}'.")

    def __add__(self: PSF, other: PSF | Array | None) -> PSF:
        """
        Allows arrays or PSFs to be added together. None values are ignored.
        """
        return self._magic_unified_op(other, "add")

    def __sub__(self: PSF, other: PSF | Array | None) -> PSF:
        """
        Allows arrays or PSFs to be subtracted. None values are ignored.
        """
        return self._magic_unified_op(other, "subtract")

    def __mul__(self: PSF, other: PSF | Array | None) -> PSF:
        """
        Allows arrays or PSFs to be multiplied. None values are ignored.
        """
        return self._magic_unified_op(other, "multiply")

    def __truediv__(self: PSF, other: PSF | Array | None) -> PSF:
        """
        Allows arrays or PSFs to be divided. None values are ignored.
        """
        return self._magic_unified_op(other, "divide")

    def __iadd__(self: PSF, other: PSF | Array | None) -> PSF:
        """In-place addition."""
        return self.__add__(other)

    def __isub__(self: PSF, other: PSF | Array | None) -> PSF:
        """In-place subtraction."""
        return self.__sub__(other)

    def __imul__(self: PSF, other: PSF | Array | None) -> PSF:
        """In-place multiplication."""
        return self.__mul__(other)

    def __itruediv__(self: PSF, other: PSF | Array | None) -> PSF:
        """In-place division."""
        return self.__truediv__(other)

ndim property ¤

Returns the number of dimensions of the PSF. This is used to track the vectorised version of the PSF returned from vmapping.

Returns:

Name Type Description
ndim int

The dimensionality of the PSF.

npixels property ¤

Returns the side length of the arrays currently representing the PSF.

Returns:

Name Type Description
npixels int

The number of pixels that represent the PSF.

__add__(other) ¤

Allows arrays or PSFs to be added together. None values are ignored.

Source code in dLux/psfs.py
206
207
208
209
210
def __add__(self: PSF, other: PSF | Array | None) -> PSF:
    """
    Allows arrays or PSFs to be added together. None values are ignored.
    """
    return self._magic_unified_op(other, "add")

__iadd__(other) ¤

In-place addition.

Source code in dLux/psfs.py
230
231
232
def __iadd__(self: PSF, other: PSF | Array | None) -> PSF:
    """In-place addition."""
    return self.__add__(other)

__imul__(other) ¤

In-place multiplication.

Source code in dLux/psfs.py
238
239
240
def __imul__(self: PSF, other: PSF | Array | None) -> PSF:
    """In-place multiplication."""
    return self.__mul__(other)

__init__(data, pixel_scale) ¤

Parameters:

Name Type Description Default
data Array

The PSF to be transformed by the detector.

required
pixel_scale Array

The pixel scale of the PSF.

required
Source code in dLux/psfs.py
36
37
38
39
40
41
42
43
44
45
46
def __init__(self: PSF, data: Array, pixel_scale: Array):
    """
    Parameters
    ----------
    data : Array
        The PSF to be transformed by the detector.
    pixel_scale : Array
        The pixel scale of the PSF.
    """
    self.data = np.asarray(data, dtype=float)
    self.pixel_scale = np.asarray(pixel_scale, dtype=float)

__isub__(other) ¤

In-place subtraction.

Source code in dLux/psfs.py
234
235
236
def __isub__(self: PSF, other: PSF | Array | None) -> PSF:
    """In-place subtraction."""
    return self.__sub__(other)

__itruediv__(other) ¤

In-place division.

Source code in dLux/psfs.py
242
243
244
def __itruediv__(self: PSF, other: PSF | Array | None) -> PSF:
    """In-place division."""
    return self.__truediv__(other)

__mul__(other) ¤

Allows arrays or PSFs to be multiplied. None values are ignored.

Source code in dLux/psfs.py
218
219
220
221
222
def __mul__(self: PSF, other: PSF | Array | None) -> PSF:
    """
    Allows arrays or PSFs to be multiplied. None values are ignored.
    """
    return self._magic_unified_op(other, "multiply")

__sub__(other) ¤

Allows arrays or PSFs to be subtracted. None values are ignored.

Source code in dLux/psfs.py
212
213
214
215
216
def __sub__(self: PSF, other: PSF | Array | None) -> PSF:
    """
    Allows arrays or PSFs to be subtracted. None values are ignored.
    """
    return self._magic_unified_op(other, "subtract")

__truediv__(other) ¤

Allows arrays or PSFs to be divided. None values are ignored.

Source code in dLux/psfs.py
224
225
226
227
228
def __truediv__(self: PSF, other: PSF | Array | None) -> PSF:
    """
    Allows arrays or PSFs to be divided. None values are ignored.
    """
    return self._magic_unified_op(other, "divide")

convolve(other, method='auto') ¤

Convolves the PSF with an input array.

Parameters:

Name Type Description Default
other Array

The array to convolve with the PSF.

required
method str = "auto"

The method to use for the convolution. Can be "auto", "direct", or "fft". Is "auto" by default, which calls "direct".

'auto'

Returns:

Name Type Description
psf PSF

The convolved PSF.

Source code in dLux/psfs.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def convolve(self: PSF, other: Array, method: str = "auto") -> PSF:
    """
    Convolves the PSF with an input array.

    Parameters
    ----------
    other : Array
        The array to convolve with the PSF.
    method : str = "auto"
        The method to use for the convolution. Can be "auto", "direct",
        or "fft". Is "auto" by default, which calls "direct".

    Returns
    -------
    psf : PSF
        The convolved PSF.
    """
    return self.set(data=convolve(self.data, other, mode="same", method=method))

downsample(n) ¤

Downsamples the PSF by a factor of n. This is done by summing the PSF pixels in n x n blocks.

Parameters:

Name Type Description Default
n int

The factor by which to downsample the PSF.

required

Returns:

Name Type Description
psf PSF

The downsampled PSF.

Source code in dLux/psfs.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def downsample(self: PSF, n: int) -> PSF:
    """
    Downsamples the PSF by a factor of n. This is done by summing the PSF pixels in
    n x n blocks.

    Parameters
    ----------
    n : int
        The factor by which to downsample the PSF.

    Returns
    -------
    psf : PSF
        The downsampled PSF.
    """
    data = dlu.downsample(self.data, n, mean=False)
    pixel_scale = self.pixel_scale * n
    return self.set(data=data, pixel_scale=pixel_scale)

flip(axis) ¤

Flips the PSF along the specified axes. Note we use 'ij' indexing, so axis 0 is the y-axis and axis 1 is the x-axis.

Parameters:

Name Type Description Default
axis tuple

The axes along which to flip the PSF.

required

Returns:

Name Type Description
psf PSF

The new flipped PSF.

Source code in dLux/psfs.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def flip(self: PSF, axis: tuple) -> PSF:
    """
    Flips the PSF along the specified axes. Note we use 'ij' indexing, so axis 0 is
    the y-axis and axis 1 is the x-axis.

    Parameters
    ----------
    axis : tuple
        The axes along which to flip the PSF.

    Returns
    -------
    psf : PSF
        The new flipped PSF.
    """
    return self.set(data=np.flip(self.data, axis))

resize(npixels) ¤

Resizes the PSF via a zero-padding or cropping operation.

Parameters:

Name Type Description Default
npixels int

The size to resize the PSF to.

required

Returns:

Name Type Description
psf PSF

The resized PSF.

Source code in dLux/psfs.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def resize(self: PSF, npixels: int) -> PSF:
    """
    Resizes the PSF via a zero-padding or cropping operation.

    Parameters
    ----------
    npixels : int
        The size to resize the PSF to.

    Returns
    -------
    psf : PSF
        The resized PSF.
    """
    return self.set(data=dlu.resize(self.data, npixels))

rotate(angle, method='linear') ¤

Rotates the PSF by a given angle via interpolation.

Parameters:

Name Type Description Default
angle float

The angle by which to rotate the PSF.

required
method str = "linear"

The interpolation method.

'linear'

Returns:

Name Type Description
psf PSF

The rotated PSF.

Source code in dLux/psfs.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def rotate(self: PSF, angle: float, method: str = "linear") -> PSF:
    """
    Rotates the PSF by a given angle via interpolation.

    Parameters
    ----------
    angle : float
        The angle by which to rotate the PSF.
    method : str = "linear"
        The interpolation method.

    Returns
    -------
    psf : PSF
        The rotated PSF.
    """
    return self.set(data=dlu.rotate(self.data, angle, method=method))