Telescope Models

The NewtonianTelescope class is the main entry point for PSF simulation. It inherits from GenericTelescope, which provides the POPPY integration layer.


NewtonianTelescope

from psfcraft import NewtonianTelescope

Minimal example:

tel = NewtonianTelescope(version="1_3")
tel.pixelscale = 0.1   # arcsec/pixel
psf = tel.calc_psf(monochromatic=1.2e-6, fov_pixels=64)

With wavefront errors:

import numpy as np
wfe = np.zeros(15)
wfe[3] = 50e-9    # 50 nm RMS defocus (Noll index 4)
wfe[6] = 100e-9   # 100 nm RMS coma (Noll index 7)

tel = NewtonianTelescope(version="1_3", wfe_coefficients=wfe)
psf = tel.calc_psf(monochromatic=1.2e-6, fov_pixels=64)

NewtonianTelescope

NewtonianTelescope(name=name, version=version, primary_radius=primary_radius, secondary_radius=secondary_radius, detector_oversample=detector_oversample, wfe_coefficients=wfe_coefficients, *args, **kwargs)

Bases: GenericTelescope

Source code in psfcraft/psfcraft_core.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def __init__(self, 
             name=name, version=version, 
             primary_radius=primary_radius,secondary_radius=secondary_radius,
             detector_oversample = detector_oversample,
             wfe_coefficients=wfe_coefficients,
             *args, **kwargs
             ):

    super().__init__(name=name, version=version,  
                     primary_radius=primary_radius,secondary_radius=secondary_radius,
                     detector_oversample = detector_oversample,
                    #  fov_arcsec = 12,
                     *args, **kwargs) 

    self.name = name 
    self.version = version

    self.primary_radius = primary_radius
    self.secondary_radius = secondary_radius

    # self.pixelscale = 0.05 # arcsec/pixel
    self.pixelscale = 0.298 # arcsec/pixel
    # self.pixelscale = 2/32 # arcsec/pixel for a 2 arcsec fov

    self.wfe_coefficients = wfe_coefficients

    from .optics import NewtonianTelescopeAperture
    self.pupil = NewtonianTelescopeAperture(optical_system_version=version,
                                            primary_radius=primary_radius,secondary_radius=secondary_radius
                                            )
    self.pupil_radius = self.pupil.primary_radius # or primary_radius

    self.options['pupil_shift_x'] = 0.
    self.options['pupil_shift_y'] = 0.

GenericTelescope

Base class — extend this to implement custom telescope geometries.

GenericTelescope

GenericTelescope(name=name, version=version, primary_radius=None, secondary_radius=None, detector_oversample=None, *args, **kwargs)

Bases: Instrument

Source code in psfcraft/psfcraft_core.py
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
def __init__(self, name=name, version=version,
             primary_radius=None,secondary_radius=None,
             detector_oversample=None,
             *args, **kwargs,
             ):
    # super().__init__(name=name, version=version,  
    #                  primary_radius=primary_radius,secondary_radius=secondary_radius,
    #                  *args, **kwargs) 

    self.name = name
    self.version = version

    self.primary_radius = primary_radius
    self.secondary_radius = secondary_radius

    self._image_mask = None
    self._pupil_mask = None

    self.pupil = None
    self.pupilopd = None  # This can optionally be set to a tuple indicating (filename, slice in datacube)

    self.pupil_radius = None  # Set when loading FITS file in _get_optical_system

    self.options = {}  # dict for storing other arbitrary options.

    # filter_list   available filter names in order by wavelength for public api
    # _filters      a dict of named tuples with name, filename, & default_nlambda with the filter name as the key
    # self.filter_list, self._filters = self._get_filter_list()
    self.filter_list, self._filters = self._get_filter_list()

    # choose a default filter, in case the user doesn't specify one
    self.filter = self.filter_list[0]

    self._rotation = None

    self._image_mask = None
    self.image_mask_list = []
    "List of available image_masks"

    self._pupil_mask = None
    self.pupil_mask_list = []
    "List of available pupil_masks"

    self.wfe_coefficients = None
    "List of WFE coefficients"

    self.pixelscale = None
    "Detector pixel scale, in arcsec/pixel"
    self._spectra_cache = {}  # for caching synphot results.

    # n.b. subclasses must set these
    self._detectors = {}
    self._detector = None
    # self._detector_npixels = 2048

    self.detector_oversample = detector_oversample

image_mask_list instance-attribute

image_mask_list = []

List of available image_masks

pixelscale class-attribute instance-attribute

pixelscale = None

Detector pixel scale, in arcsec/pixel

pupil_mask_list instance-attribute

pupil_mask_list = []

List of available pupil_masks

wfe_coefficients class-attribute instance-attribute

wfe_coefficients = None

List of WFE coefficients