Skip to content

Optical Systems

OpticalSystem

Bases: BaseOpticalSystem

Base optics class implementing both the propagate and model methods that are universal to all optics classes.

Source code in src/dLux/optical_systems.py
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
class OpticalSystem(BaseOpticalSystem):
    """
    Base optics class implementing both the `propagate` and `model` methods that are
    universal to all optics classes.
    """

    def propagate(
        self: OpticalSystem,
        wavelengths: Array,
        offset: Array = np.zeros(2),
        weights: Array = None,
        return_wf: bool = False,
        return_psf: bool = False,
    ) -> Array:
        """
        Propagates a Polychromatic point source through the optics.

        Parameters
        ----------
        wavelengths : Array, metres
            The wavelengths of the wavefronts to propagate through the optics.
        offset : Array, radians = np.zeros(2)
            The (x, y) offset from the optical axis of the source.
        weights : Array = None
            The weight of each wavelength. If None, all weights are equal.
        return_wf : bool = False
            Should the Wavefront object be returned instead of the psf Array?
        return_psf : bool = False
            Should the PSF object be returned instead of the psf Array?

        Returns
        -------
        object : Array, Wavefront, PSF
            if `return_wf` is False and `return_psf` is False, returns the psf Array.
            if `return_wf` is True and `return_psf` is False, returns the Wavefront
                object.
            if `return_wf` is False and `return_psf` is True, returns the PSF object.
        """
        if return_wf and return_psf:
            raise ValueError(
                "return_wf and return_psf cannot both be True. "
                "Please choose one."
            )

        wavelengths = np.atleast_1d(wavelengths)
        if weights is None:
            weights = np.ones_like(wavelengths) / len(wavelengths)
        else:
            weights = np.atleast_1d(weights)

        # Check wavelengths and weights
        if weights.shape != wavelengths.shape:
            raise ValueError(
                "wavelengths and weights must have the "
                f"same shape, got {wavelengths.shape} and {weights.shape} "
                "respectively."
            )

        # Check offset
        offset = np.array(offset) if not isinstance(offset, Array) else offset
        if offset.shape != (2,):
            raise ValueError(
                "offset must be a 2-element array, got "
                f"shape {offset.shape}."
            )

        # Calculate - note we multiply by sqrt(weight) to account for the
        # fact that the PSF is the square of the amplitude
        prop_fn = lambda wavelength, weight: self.propagate_mono(
            wavelength, offset, return_wf=True
        ).multiply("amplitude", weight**0.5)
        wf = filter_vmap(prop_fn)(wavelengths, weights)

        # Return PSF, Wavefront, or array psf
        if return_wf:
            return wf
        if return_psf:
            return PSF(wf.psf.sum(0), wf.pixel_scale.mean())
        return wf.psf.sum(0)

    def model(
        self: OpticalSystem,
        source: Source,
        return_wf: bool = False,
        return_psf: bool = False,
    ) -> Array:
        """
        Models the input Source object through the optics.

        Parameters
        ----------
        source : Source
            The Source object to model through the optics.
        return_wf : bool = False
            Should the Wavefront object be returned instead of the psf Array?
        return_psf : bool = False
            Should the PSF object be returned instead of the psf Array?

        Returns
        -------
        object : Array, Wavefront, PSF
            if `return_wf` is False and `return_psf` is False, returns the psf Array.
            if `return_wf` is True and `return_psf` is False, returns the Wavefront
                object.
            if `return_wf` is False and `return_psf` is True, returns the PSF object.
        """
        return source.model(self, return_wf, return_psf)

model(source, return_wf=False, return_psf=False)

Models the input Source object through the optics.

Parameters:

Name Type Description Default
source BaseSource

The Source object to model through the optics.

required
return_wf bool = False

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

False
return_psf bool = False

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

False

Returns:

Name Type Description
object (Array, Wavefront, PSF)

if return_wf is False and return_psf is False, returns the psf Array. if return_wf is True and return_psf is False, returns the Wavefront object. if return_wf is False and return_psf is True, returns the PSF object.

Source code in src/dLux/optical_systems.py
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
def model(
    self: OpticalSystem,
    source: Source,
    return_wf: bool = False,
    return_psf: bool = False,
) -> Array:
    """
    Models the input Source object through the optics.

    Parameters
    ----------
    source : Source
        The Source object to model through the optics.
    return_wf : bool = False
        Should the Wavefront object be returned instead of the psf Array?
    return_psf : bool = False
        Should the PSF object be returned instead of the psf Array?

    Returns
    -------
    object : Array, Wavefront, PSF
        if `return_wf` is False and `return_psf` is False, returns the psf Array.
        if `return_wf` is True and `return_psf` is False, returns the Wavefront
            object.
        if `return_wf` is False and `return_psf` is True, returns the PSF object.
    """
    return source.model(self, return_wf, return_psf)

propagate(wavelengths, offset=np.zeros(2), weights=None, return_wf=False, return_psf=False)

Propagates a Polychromatic point source through the optics.

Parameters:

Name Type Description Default
wavelengths (Array, metres)

The wavelengths of the wavefronts to propagate through the optics.

required
offset Array, radians = np.zeros(2)

The (x, y) offset from the optical axis of the source.

zeros(2)
weights Array = None

The weight of each wavelength. If None, all weights are equal.

None
return_wf bool = False

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

False
return_psf bool = False

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

False

Returns:

Name Type Description
object (Array, Wavefront, PSF)

if return_wf is False and return_psf is False, returns the psf Array. if return_wf is True and return_psf is False, returns the Wavefront object. if return_wf is False and return_psf is True, returns the PSF object.

Source code in src/dLux/optical_systems.py
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
def propagate(
    self: OpticalSystem,
    wavelengths: Array,
    offset: Array = np.zeros(2),
    weights: Array = None,
    return_wf: bool = False,
    return_psf: bool = False,
) -> Array:
    """
    Propagates a Polychromatic point source through the optics.

    Parameters
    ----------
    wavelengths : Array, metres
        The wavelengths of the wavefronts to propagate through the optics.
    offset : Array, radians = np.zeros(2)
        The (x, y) offset from the optical axis of the source.
    weights : Array = None
        The weight of each wavelength. If None, all weights are equal.
    return_wf : bool = False
        Should the Wavefront object be returned instead of the psf Array?
    return_psf : bool = False
        Should the PSF object be returned instead of the psf Array?

    Returns
    -------
    object : Array, Wavefront, PSF
        if `return_wf` is False and `return_psf` is False, returns the psf Array.
        if `return_wf` is True and `return_psf` is False, returns the Wavefront
            object.
        if `return_wf` is False and `return_psf` is True, returns the PSF object.
    """
    if return_wf and return_psf:
        raise ValueError(
            "return_wf and return_psf cannot both be True. "
            "Please choose one."
        )

    wavelengths = np.atleast_1d(wavelengths)
    if weights is None:
        weights = np.ones_like(wavelengths) / len(wavelengths)
    else:
        weights = np.atleast_1d(weights)

    # Check wavelengths and weights
    if weights.shape != wavelengths.shape:
        raise ValueError(
            "wavelengths and weights must have the "
            f"same shape, got {wavelengths.shape} and {weights.shape} "
            "respectively."
        )

    # Check offset
    offset = np.array(offset) if not isinstance(offset, Array) else offset
    if offset.shape != (2,):
        raise ValueError(
            "offset must be a 2-element array, got "
            f"shape {offset.shape}."
        )

    # Calculate - note we multiply by sqrt(weight) to account for the
    # fact that the PSF is the square of the amplitude
    prop_fn = lambda wavelength, weight: self.propagate_mono(
        wavelength, offset, return_wf=True
    ).multiply("amplitude", weight**0.5)
    wf = filter_vmap(prop_fn)(wavelengths, weights)

    # Return PSF, Wavefront, or array psf
    if return_wf:
        return wf
    if return_psf:
        return PSF(wf.psf.sum(0), wf.pixel_scale.mean())
    return wf.psf.sum(0)
AngularOpticalSystem

Bases: ParametricOpticalSystem, LayeredOpticalSystem

An extension to the LayeredOpticalSystem class that propagates a wavefront to an image plane with psf_pixel_scale in units of arcseconds.

UML

UML

Attributes:

Name Type Description
wf_npixels int

The number of pixels representing the wavefront.

diameter (Array, metres)

The diameter of the initial wavefront to propagate.

layers OrderedDict

A series of OpticalLayer transformations to apply to wavefronts.

psf_npixels int

The number of pixels of the final PSF.

psf_pixel_scale (float, arcseconds)

The pixel scale of the final PSF.

oversample int

The oversampling factor of the final PSF. Decreases the psf_pixel_scale parameter while increasing the psf_npixels parameter.

Source code in src/dLux/optical_systems.py
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
class AngularOpticalSystem(ParametricOpticalSystem, LayeredOpticalSystem):
    """
    An extension to the LayeredOpticalSystem class that propagates a wavefront to an
    image plane with `psf_pixel_scale` in units of arcseconds.

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

    Attributes
    ----------
    wf_npixels : int
        The number of pixels representing the wavefront.
    diameter : Array, metres
        The diameter of the initial wavefront to propagate.
    layers : OrderedDict
        A series of `OpticalLayer` transformations to apply to wavefronts.
    psf_npixels : int
        The number of pixels of the final PSF.
    psf_pixel_scale : float, arcseconds
        The pixel scale of the final PSF.
    oversample : int
        The oversampling factor of the final PSF. Decreases the psf_pixel_scale
        parameter while increasing the psf_npixels parameter.
    """

    def __init__(
        self: OpticalSystem,
        wf_npixels: int,
        diameter: float,
        layers: list[OpticalLayer, tuple],
        psf_npixels: int,
        psf_pixel_scale: float,
        oversample: int = 1,
    ):
        """
        Parameters
        ----------
        wf_npixels : int
            The number of pixels representing the wavefront.
        diameter : Array, metres
            The diameter of the initial wavefront to propagate.
        layers : list[OpticalLayer, tuple]
            A list of `OpticalLayer` transformations to apply to wavefronts. The list
            entries can be either `OpticalLayer` objects or tuples of (key, layer) to
            specify a key for the layer in the layers dictionary.
        psf_npixels : int
            The number of pixels of the final PSF.
        psf_pixel_scale : float, arcseconds
            The pixel scale of the final PSF in units of arcseconds.
        oversample : int
            The oversampling factor of the final PSF. Decreases the psf_pixel_scale
            parameter while increasing the psf_npixels parameter.
        """
        super().__init__(
            wf_npixels=wf_npixels,
            diameter=diameter,
            layers=layers,
            psf_npixels=psf_npixels,
            psf_pixel_scale=psf_pixel_scale,
            oversample=oversample,
        )

    def propagate_mono(
        self: OpticalSystem,
        wavelength: Array,
        offset: Array = np.zeros(2),
        return_wf: bool = False,
    ) -> Array:
        """
        Propagates a monochromatic point source through the optical layers.

        Parameters
        ----------
        wavelength : float, metres
            The wavelength of the wavefront to propagate through the optical layers.
        offset : Array, radians = np.zeros(2)
            The (x, y) offset from the optical axis of the source.
        return_wf: bool = False
            Should the Wavefront object be returned instead of the psf Array?

        Returns
        -------
        object : Array, Wavefront
            if `return_wf` is False, returns the psf Array.
            if `return_wf` is True, returns the Wavefront object.
        """
        wf = super().propagate_mono(wavelength, offset, return_wf=True)

        # Propagate
        true_pixel_scale = self.psf_pixel_scale / self.oversample
        pixel_scale = dlu.arcsec2rad(true_pixel_scale)
        psf_npixels = self.psf_npixels * self.oversample
        wf = wf.propagate(psf_npixels, pixel_scale)

        # Return PSF or Wavefront
        if return_wf:
            return wf
        return wf.psf

__init__(wf_npixels, diameter, layers, psf_npixels, psf_pixel_scale, oversample=1)

Parameters:

Name Type Description Default
wf_npixels int

The number of pixels representing the wavefront.

required
diameter (Array, metres)

The diameter of the initial wavefront to propagate.

required
layers list[OpticalLayer, tuple]

A list of OpticalLayer transformations to apply to wavefronts. The list entries can be either OpticalLayer objects or tuples of (key, layer) to specify a key for the layer in the layers dictionary.

required
psf_npixels int

The number of pixels of the final PSF.

required
psf_pixel_scale (float, arcseconds)

The pixel scale of the final PSF in units of arcseconds.

required
oversample int

The oversampling factor of the final PSF. Decreases the psf_pixel_scale parameter while increasing the psf_npixels parameter.

1
Source code in src/dLux/optical_systems.py
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
def __init__(
    self: OpticalSystem,
    wf_npixels: int,
    diameter: float,
    layers: list[OpticalLayer, tuple],
    psf_npixels: int,
    psf_pixel_scale: float,
    oversample: int = 1,
):
    """
    Parameters
    ----------
    wf_npixels : int
        The number of pixels representing the wavefront.
    diameter : Array, metres
        The diameter of the initial wavefront to propagate.
    layers : list[OpticalLayer, tuple]
        A list of `OpticalLayer` transformations to apply to wavefronts. The list
        entries can be either `OpticalLayer` objects or tuples of (key, layer) to
        specify a key for the layer in the layers dictionary.
    psf_npixels : int
        The number of pixels of the final PSF.
    psf_pixel_scale : float, arcseconds
        The pixel scale of the final PSF in units of arcseconds.
    oversample : int
        The oversampling factor of the final PSF. Decreases the psf_pixel_scale
        parameter while increasing the psf_npixels parameter.
    """
    super().__init__(
        wf_npixels=wf_npixels,
        diameter=diameter,
        layers=layers,
        psf_npixels=psf_npixels,
        psf_pixel_scale=psf_pixel_scale,
        oversample=oversample,
    )

propagate_mono(wavelength, offset=np.zeros(2), return_wf=False)

Propagates a monochromatic point source through the optical layers.

Parameters:

Name Type Description Default
wavelength (float, metres)

The wavelength of the wavefront to propagate through the optical layers.

required
offset Array, radians = np.zeros(2)

The (x, y) offset from the optical axis of the source.

zeros(2)
return_wf bool

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

False

Returns:

Name Type Description
object (Array, Wavefront)

if return_wf is False, returns the psf Array. if return_wf is True, returns the Wavefront object.

Source code in src/dLux/optical_systems.py
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
def propagate_mono(
    self: OpticalSystem,
    wavelength: Array,
    offset: Array = np.zeros(2),
    return_wf: bool = False,
) -> Array:
    """
    Propagates a monochromatic point source through the optical layers.

    Parameters
    ----------
    wavelength : float, metres
        The wavelength of the wavefront to propagate through the optical layers.
    offset : Array, radians = np.zeros(2)
        The (x, y) offset from the optical axis of the source.
    return_wf: bool = False
        Should the Wavefront object be returned instead of the psf Array?

    Returns
    -------
    object : Array, Wavefront
        if `return_wf` is False, returns the psf Array.
        if `return_wf` is True, returns the Wavefront object.
    """
    wf = super().propagate_mono(wavelength, offset, return_wf=True)

    # Propagate
    true_pixel_scale = self.psf_pixel_scale / self.oversample
    pixel_scale = dlu.arcsec2rad(true_pixel_scale)
    psf_npixels = self.psf_npixels * self.oversample
    wf = wf.propagate(psf_npixels, pixel_scale)

    # Return PSF or Wavefront
    if return_wf:
        return wf
    return wf.psf
CartesianOpticalSystem

Bases: ParametricOpticalSystem, LayeredOpticalSystem

An extension to the LayeredOpticalSystem class that propagates a wavefront to an image plane with psf_pixel_scale in units of microns.

UML

UML

Attributes:

Name Type Description
wf_npixels int

The number of pixels representing the wavefront.

diameter (Array, metres)

The diameter of the initial wavefront to propagate.

layers OrderedDict

A series of OpticalLayer transformations to apply to wavefronts.

focal_length (float, metres)

The focal length of the system.

psf_npixels int

The number of pixels of the final PSF.

psf_pixel_scale (float, microns)

The pixel scale of the final PSF.

oversample int

The oversampling factor of the final PSF. Decreases the psf_pixel_scale parameter while increasing the psf_npixels parameter.

Source code in src/dLux/optical_systems.py
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
class CartesianOpticalSystem(ParametricOpticalSystem, LayeredOpticalSystem):
    """
    An extension to the LayeredOpticalSystem class that propagates a wavefront to an
    image plane with `psf_pixel_scale` in units of microns.

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

    Attributes
    ----------
    wf_npixels : int
        The number of pixels representing the wavefront.
    diameter : Array, metres
        The diameter of the initial wavefront to propagate.
    layers : OrderedDict
        A series of `OpticalLayer` transformations to apply to wavefronts.
    focal_length : float, metres
        The focal length of the system.
    psf_npixels : int
        The number of pixels of the final PSF.
    psf_pixel_scale : float, microns
        The pixel scale of the final PSF.
    oversample : int
        The oversampling factor of the final PSF. Decreases the psf_pixel_scale
        parameter while increasing the psf_npixels parameter.
    """

    focal_length: None

    def __init__(
        self: OpticalSystem,
        wf_npixels: int,
        diameter: float,
        layers: list[OpticalLayer, tuple],
        focal_length: float,
        psf_npixels: int,
        psf_pixel_scale: float,
        oversample: int = 1,
    ):
        """
        Parameters
        ----------
        wf_npixels : int
            The number of pixels representing the wavefront.
        diameter : Array, metres
            The diameter of the initial wavefront to propagate.
        layers : list[OpticalLayer, tuple]
            A list of `OpticalLayer` transformations to apply to wavefronts. The list
            entries can be either `OpticalLayer` objects or tuples of (key, layer) to
            specify a key for the layer in the layers dictionary.
        focal_length : float, metres
            The focal length of the system.
        psf_npixels : int
            The number of pixels of the final PSF.
        psf_pixel_scale : float, microns
            The pixel scale of the final PSF in units of microns.
        oversample : int
            The oversampling factor of the final PSF. Decreases the psf_pixel_scale
            parameter while increasing the psf_npixels parameter.
        """
        self.focal_length = float(focal_length)

        super().__init__(
            wf_npixels=wf_npixels,
            diameter=diameter,
            layers=layers,
            psf_npixels=psf_npixels,
            psf_pixel_scale=psf_pixel_scale,
            oversample=oversample,
        )

    def propagate_mono(
        self: OpticalSystem,
        wavelength: Array,
        offset: Array = np.zeros(2),
        return_wf: bool = False,
    ) -> Array:
        """
        Propagates a monochromatic point source through the optical layers.

        Parameters
        ----------
        wavelength : float, metres
            The wavelength of the wavefront to propagate through the optical layers.
        offset : Array, radians = np.zeros(2)
            The (x, y) offset from the optical axis of the source.
        return_wf: bool = False
            Should the Wavefront object be returned instead of the psf Array?

        Returns
        -------
        object : Array, Wavefront
            if `return_wf` is False, returns the psf Array.
            if `return_wf` is True, returns the Wavefront object.
        """
        wf = super().propagate_mono(wavelength, offset, return_wf=True)

        # Propagate
        true_pixel_scale = self.psf_pixel_scale / self.oversample
        pixel_scale = 1e-6 * true_pixel_scale
        psf_npixels = self.psf_npixels * self.oversample
        wf = wf.propagate(psf_npixels, pixel_scale, self.focal_length)

        # Return PSF or Wavefront
        if return_wf:
            return wf
        return wf.psf

__init__(wf_npixels, diameter, layers, focal_length, psf_npixels, psf_pixel_scale, oversample=1)

Parameters:

Name Type Description Default
wf_npixels int

The number of pixels representing the wavefront.

required
diameter (Array, metres)

The diameter of the initial wavefront to propagate.

required
layers list[OpticalLayer, tuple]

A list of OpticalLayer transformations to apply to wavefronts. The list entries can be either OpticalLayer objects or tuples of (key, layer) to specify a key for the layer in the layers dictionary.

required
focal_length (float, metres)

The focal length of the system.

required
psf_npixels int

The number of pixels of the final PSF.

required
psf_pixel_scale (float, microns)

The pixel scale of the final PSF in units of microns.

required
oversample int

The oversampling factor of the final PSF. Decreases the psf_pixel_scale parameter while increasing the psf_npixels parameter.

1
Source code in src/dLux/optical_systems.py
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
def __init__(
    self: OpticalSystem,
    wf_npixels: int,
    diameter: float,
    layers: list[OpticalLayer, tuple],
    focal_length: float,
    psf_npixels: int,
    psf_pixel_scale: float,
    oversample: int = 1,
):
    """
    Parameters
    ----------
    wf_npixels : int
        The number of pixels representing the wavefront.
    diameter : Array, metres
        The diameter of the initial wavefront to propagate.
    layers : list[OpticalLayer, tuple]
        A list of `OpticalLayer` transformations to apply to wavefronts. The list
        entries can be either `OpticalLayer` objects or tuples of (key, layer) to
        specify a key for the layer in the layers dictionary.
    focal_length : float, metres
        The focal length of the system.
    psf_npixels : int
        The number of pixels of the final PSF.
    psf_pixel_scale : float, microns
        The pixel scale of the final PSF in units of microns.
    oversample : int
        The oversampling factor of the final PSF. Decreases the psf_pixel_scale
        parameter while increasing the psf_npixels parameter.
    """
    self.focal_length = float(focal_length)

    super().__init__(
        wf_npixels=wf_npixels,
        diameter=diameter,
        layers=layers,
        psf_npixels=psf_npixels,
        psf_pixel_scale=psf_pixel_scale,
        oversample=oversample,
    )

propagate_mono(wavelength, offset=np.zeros(2), return_wf=False)

Propagates a monochromatic point source through the optical layers.

Parameters:

Name Type Description Default
wavelength (float, metres)

The wavelength of the wavefront to propagate through the optical layers.

required
offset Array, radians = np.zeros(2)

The (x, y) offset from the optical axis of the source.

zeros(2)
return_wf bool

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

False

Returns:

Name Type Description
object (Array, Wavefront)

if return_wf is False, returns the psf Array. if return_wf is True, returns the Wavefront object.

Source code in src/dLux/optical_systems.py
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
def propagate_mono(
    self: OpticalSystem,
    wavelength: Array,
    offset: Array = np.zeros(2),
    return_wf: bool = False,
) -> Array:
    """
    Propagates a monochromatic point source through the optical layers.

    Parameters
    ----------
    wavelength : float, metres
        The wavelength of the wavefront to propagate through the optical layers.
    offset : Array, radians = np.zeros(2)
        The (x, y) offset from the optical axis of the source.
    return_wf: bool = False
        Should the Wavefront object be returned instead of the psf Array?

    Returns
    -------
    object : Array, Wavefront
        if `return_wf` is False, returns the psf Array.
        if `return_wf` is True, returns the Wavefront object.
    """
    wf = super().propagate_mono(wavelength, offset, return_wf=True)

    # Propagate
    true_pixel_scale = self.psf_pixel_scale / self.oversample
    pixel_scale = 1e-6 * true_pixel_scale
    psf_npixels = self.psf_npixels * self.oversample
    wf = wf.propagate(psf_npixels, pixel_scale, self.focal_length)

    # Return PSF or Wavefront
    if return_wf:
        return wf
    return wf.psf
LayeredOpticalSystem

Bases: OpticalSystem

A flexible optical system that allows for the arbitrary chaining of OpticalLayers.

UML

UML

Attributes:

Name Type Description
wf_npixels int

The size of the initial wavefront to propagate.

diameter (float, metres)

The diameter of the wavefront to propagate.

layers OrderedDict

A series of OpticalLayer transformations to apply to wavefronts.

Source code in src/dLux/optical_systems.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
class LayeredOpticalSystem(OpticalSystem):
    """
    A flexible optical system that allows for the arbitrary chaining of OpticalLayers.

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

    Attributes
    ----------
    wf_npixels : int
        The size of the initial wavefront to propagate.
    diameter : float, metres
        The diameter of the wavefront to propagate.
    layers : OrderedDict
        A series of `OpticalLayer` transformations to apply to wavefronts.
    """

    wf_npixels: int
    diameter: float
    layers: OrderedDict

    def __init__(
        self: OpticalSystem,
        wf_npixels: int,
        diameter: float,
        layers: list[OpticalLayer, tuple],
    ):
        """
        Parameters
        ----------
        wf_npixels : int
            The size of the initial wavefront to propagate.
        diameter : float
            The diameter of the wavefront to propagate.
        layers : list[OpticalLayer, tuple]
            A list of `OpticalLayer` transformations to apply to wavefronts. The list
            entries can be either `OpticalLayer` objects or tuples of (key, layer) to
            specify a key for the layer in the layers dictionary.
        """
        self.wf_npixels = int(wf_npixels)
        self.diameter = float(diameter)
        self.layers = dlu.list2dictionary(layers, True, OpticalLayer)

    def __getattr__(self: OpticalSystem, key: str) -> Any:
        """
        Raises both the individual layers and the attributes of the layers via
        their keys.

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

        Returns
        -------
        item : object
            The item corresponding to the supplied key in the layers dictionary.
        """
        if key in self.layers.keys():
            return self.layers[key]
        for layer in list(self.layers.values()):
            if hasattr(layer, key):
                return getattr(layer, key)
        raise AttributeError(
            f"{self.__class__.__name__} has no attribute " f"{key}."
        )

    def propagate_mono(
        self: OpticalSystem,
        wavelength: Array,
        offset: Array = np.zeros(2),
        return_wf: bool = False,
    ) -> Array:
        """
        Propagates a monochromatic point source through the optical layers.

        Parameters
        ----------
        wavelength : float, metres
            The wavelength of the wavefront to propagate through the optical layers.
        offset : Array, radians = np.zeros(2)
            The (x, y) offset from the optical axis of the source.
        return_wf: bool = False
            Should the Wavefront object be returned instead of the psf Array?

        Returns
        -------
        object : Array, Wavefront
            if `return_wf` is False, returns the psf Array.
            if `return_wf` is True, returns the Wavefront object.
        """
        # Initialise wavefront
        wavefront = Wavefront(self.wf_npixels, self.diameter, wavelength)
        wavefront = wavefront.tilt(offset)

        # Apply layers
        for layer in list(self.layers.values()):
            wavefront *= layer

        # Return PSF or Wavefront
        if return_wf:
            return wavefront
        return wavefront.psf

    def insert_layer(
        self: OpticalSystem, layer: Union[OpticalLayer, tuple], index: int
    ) -> OpticalSystem:
        """
        Inserts a layer into the layers dictionary at a specified index. This function
        calls the list2dictionary function to ensure all keys remain unique. Note that
        this can result in some keys being modified if they are duplicates. The input
        'layer' can be a tuple of (key, layer) to specify a key, else the key is taken
        as the class name of the layer.

        Parameters
        ----------
        layer : Any
            The layer to be inserted.
        index : int
            The index at which to insert the layer.

        Returns
        -------
        optical_system : OpticalSystem
            The updated optical system.
        """
        return self.set(
            "layers", dlu.insert_layer(self.layers, layer, index, OpticalLayer)
        )

    def remove_layer(self: OpticalLayer, key: str) -> OpticalSystem:
        """
        Removes a layer from the layers dictionary, specified by its key.

        Parameters
        ----------
        key : str
            The key of the layer to be removed.

        Returns
        -------
        optical_system : OpticalSystem
            The updated optical system.
        """
        return self.set("layers", dlu.remove_layer(self.layers, key))

__getattr__(key)

Raises both the individual layers and the attributes of the layers via their keys.

Parameters:

Name Type Description Default
key str

The key of the item to be searched for in the layers dictionary.

required

Returns:

Name Type Description
item object

The item corresponding to the supplied key in the layers dictionary.

Source code in src/dLux/optical_systems.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
def __getattr__(self: OpticalSystem, key: str) -> Any:
    """
    Raises both the individual layers and the attributes of the layers via
    their keys.

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

    Returns
    -------
    item : object
        The item corresponding to the supplied key in the layers dictionary.
    """
    if key in self.layers.keys():
        return self.layers[key]
    for layer in list(self.layers.values()):
        if hasattr(layer, key):
            return getattr(layer, key)
    raise AttributeError(
        f"{self.__class__.__name__} has no attribute " f"{key}."
    )

__init__(wf_npixels, diameter, layers)

Parameters:

Name Type Description Default
wf_npixels int

The size of the initial wavefront to propagate.

required
diameter float

The diameter of the wavefront to propagate.

required
layers list[OpticalLayer, tuple]

A list of OpticalLayer transformations to apply to wavefronts. The list entries can be either OpticalLayer objects or tuples of (key, layer) to specify a key for the layer in the layers dictionary.

required
Source code in src/dLux/optical_systems.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
def __init__(
    self: OpticalSystem,
    wf_npixels: int,
    diameter: float,
    layers: list[OpticalLayer, tuple],
):
    """
    Parameters
    ----------
    wf_npixels : int
        The size of the initial wavefront to propagate.
    diameter : float
        The diameter of the wavefront to propagate.
    layers : list[OpticalLayer, tuple]
        A list of `OpticalLayer` transformations to apply to wavefronts. The list
        entries can be either `OpticalLayer` objects or tuples of (key, layer) to
        specify a key for the layer in the layers dictionary.
    """
    self.wf_npixels = int(wf_npixels)
    self.diameter = float(diameter)
    self.layers = dlu.list2dictionary(layers, True, OpticalLayer)

insert_layer(layer, index)

Inserts a layer into the layers dictionary at a specified index. This function calls the list2dictionary function to ensure all keys remain unique. Note that this can result in some keys being modified if they are duplicates. The input 'layer' can be a tuple of (key, layer) to specify a key, else the key is taken as the class name of the layer.

Parameters:

Name Type Description Default
layer Any

The layer to be inserted.

required
index int

The index at which to insert the layer.

required

Returns:

Name Type Description
optical_system OpticalSystem

The updated optical system.

Source code in src/dLux/optical_systems.py
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
def insert_layer(
    self: OpticalSystem, layer: Union[OpticalLayer, tuple], index: int
) -> OpticalSystem:
    """
    Inserts a layer into the layers dictionary at a specified index. This function
    calls the list2dictionary function to ensure all keys remain unique. Note that
    this can result in some keys being modified if they are duplicates. The input
    'layer' can be a tuple of (key, layer) to specify a key, else the key is taken
    as the class name of the layer.

    Parameters
    ----------
    layer : Any
        The layer to be inserted.
    index : int
        The index at which to insert the layer.

    Returns
    -------
    optical_system : OpticalSystem
        The updated optical system.
    """
    return self.set(
        "layers", dlu.insert_layer(self.layers, layer, index, OpticalLayer)
    )

propagate_mono(wavelength, offset=np.zeros(2), return_wf=False)

Propagates a monochromatic point source through the optical layers.

Parameters:

Name Type Description Default
wavelength (float, metres)

The wavelength of the wavefront to propagate through the optical layers.

required
offset Array, radians = np.zeros(2)

The (x, y) offset from the optical axis of the source.

zeros(2)
return_wf bool

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

False

Returns:

Name Type Description
object (Array, Wavefront)

if return_wf is False, returns the psf Array. if return_wf is True, returns the Wavefront object.

Source code in src/dLux/optical_systems.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
def propagate_mono(
    self: OpticalSystem,
    wavelength: Array,
    offset: Array = np.zeros(2),
    return_wf: bool = False,
) -> Array:
    """
    Propagates a monochromatic point source through the optical layers.

    Parameters
    ----------
    wavelength : float, metres
        The wavelength of the wavefront to propagate through the optical layers.
    offset : Array, radians = np.zeros(2)
        The (x, y) offset from the optical axis of the source.
    return_wf: bool = False
        Should the Wavefront object be returned instead of the psf Array?

    Returns
    -------
    object : Array, Wavefront
        if `return_wf` is False, returns the psf Array.
        if `return_wf` is True, returns the Wavefront object.
    """
    # Initialise wavefront
    wavefront = Wavefront(self.wf_npixels, self.diameter, wavelength)
    wavefront = wavefront.tilt(offset)

    # Apply layers
    for layer in list(self.layers.values()):
        wavefront *= layer

    # Return PSF or Wavefront
    if return_wf:
        return wavefront
    return wavefront.psf

remove_layer(key)

Removes a layer from the layers dictionary, specified by its key.

Parameters:

Name Type Description Default
key str

The key of the layer to be removed.

required

Returns:

Name Type Description
optical_system OpticalSystem

The updated optical system.

Source code in src/dLux/optical_systems.py
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
def remove_layer(self: OpticalLayer, key: str) -> OpticalSystem:
    """
    Removes a layer from the layers dictionary, specified by its key.

    Parameters
    ----------
    key : str
        The key of the layer to be removed.

    Returns
    -------
    optical_system : OpticalSystem
        The updated optical system.
    """
    return self.set("layers", dlu.remove_layer(self.layers, key))