PSFCraft

Home

  • Overview
  • Getting Started
  • Web Interface
  • Generation Scripts
  • Publications
  • People
  • Funding

Tutorials

  • PSFCraft — Introduction
  • Getting Started with PSFCraft
  • Aperture Geometry and Pupil Configurations
    • Background
    • 1. Effect of the Number of Spider Struts
    • 2. Special Aperture Geometries
    • 3. Effect of the Secondary Obstruction Ratio
    • Key Takeaways
  • Wavefront Aberrations with Zernike Polynomials
  • Encircled Energy Analysis
  • Polychromatic PSF Simulation
  • End-to-End PSF Dataset Generation Pipeline

API Reference

  • Overview
  • Telescope Models
  • Aperture & Optics
  • Display & Metrics
  • Source Building
  • Batch Generation
  • Constants
PSFCraft
  • Tutorials
  • Aperture Geometry and Pupil Configurations

Aperture Geometry and Pupil Configurations¶

What you will learn:

  • How the secondary mirror obstruction affects the PSF
  • How support spider struts create diffraction spikes
  • How to compare different aperture versions side-by-side

Prerequisites: 02_getting_started.ipynb


Background¶

The pupil is the 2-D mask that describes which parts of the incoming wavefront reach the detector.
For a Newtonian telescope it has three components:

  1. Primary mirror — a circular aperture of radius $R_1$
  2. Secondary mirror — a circular obstruction of radius $R_2$ (blocks the central area)
  3. Spider struts — thin supports holding the secondary; they produce diffraction spikes

PSFCraft encodes these as NewtonianTelescopeAperture with a version string:

Version Geometry
'0' No secondary, no struts (ideal circular aperture)
'1_N' $N$ equally-spaced straight struts (e.g. '1_3', '1_4')
'2' Euclid-like asymmetric strut placement
'3' 4 cross-shaped struts
'5' 4 tangent-offset struts
'6' 8 star-shaped struts
In [1]:
Copied!
%matplotlib inline
import psfcraft
import matplotlib.pyplot as plt
%matplotlib inline import psfcraft import matplotlib.pyplot as plt
pysynphot is not installed. Please install it to use PSF generation with polychromatic sources.

1. Effect of the Number of Spider Struts¶

We iterate over version = '1_N' for $N = 0 \ldots 5$ struts and display both the pupil and the resulting PSF.

In [2]:
Copied!
n_configs = 6   # N = 0 … 5 struts
wavelength = 1e-6  # 1 µm

fig, axes = plt.subplots(2, n_configs, figsize=(18, 6))
plt.subplots_adjust(hspace=0.15, wspace=0.15)

for col, n_struts in enumerate(range(n_configs)):
    version = f"1_{n_struts}"
    tel = psfcraft.NewtonianTelescope(version=version)
    tel.pixelscale = 0.05
    psf = tel.calc_psf(monochromatic=wavelength, fov_pixels=64)

    # Row 0: pupil
    tel.optsys.display(
        what="intensity", ax=axes[0, col],
        title=f"{n_struts} struts",
        colorbar=False,
    )
    # Row 1: PSF
    psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False)
    axes[0, col].set_xlabel("")

axes[0, 0].set_ylabel("Pupil")
axes[1, 0].set_ylabel("PSF")
plt.suptitle("Pupil geometry and PSF  —  varying number of spider struts", fontsize=13)
plt.show()
n_configs = 6 # N = 0 … 5 struts wavelength = 1e-6 # 1 µm fig, axes = plt.subplots(2, n_configs, figsize=(18, 6)) plt.subplots_adjust(hspace=0.15, wspace=0.15) for col, n_struts in enumerate(range(n_configs)): version = f"1_{n_struts}" tel = psfcraft.NewtonianTelescope(version=version) tel.pixelscale = 0.05 psf = tel.calc_psf(monochromatic=wavelength, fov_pixels=64) # Row 0: pupil tel.optsys.display( what="intensity", ax=axes[0, col], title=f"{n_struts} struts", colorbar=False, ) # Row 1: PSF psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False) axes[0, col].set_xlabel("") axes[0, 0].set_ylabel("Pupil") axes[1, 0].set_ylabel("PSF") plt.suptitle("Pupil geometry and PSF — varying number of spider struts", fontsize=13) plt.show()
No description has been provided for this image

Interpretation:

  • Each strut creates two symmetric diffraction spikes separated by 180°.
  • 0 struts → no spikes; pure Airy-ring pattern with a central dark spot from the secondary.
  • 3 struts → 6-spike pattern (most common in small telescopes).
  • 4 struts → familiar 4-spike cross (Hubble Space Telescope style).

2. Special Aperture Geometries¶

PSFCraft also ships a few hand-crafted aperture models that mimic real telescope pupils.

In [3]:
Copied!
special_versions = {
    "0":   "No spider\n(ideal)",
    "1_3": "3-strut\n(classic)",
    "1_4": "4-strut\n(cross)",
    "3":   "4-strut\n(v3)",
    "5":   "4-strut\n(tangent offset)",
    "6":   "8-strut\n(star shape)",
}

fig, axes = plt.subplots(2, len(special_versions), figsize=(18, 6))
plt.subplots_adjust(hspace=0.15, wspace=0.15)

for col, (ver, label) in enumerate(special_versions.items()):
    tel = psfcraft.NewtonianTelescope(version=ver)
    tel.pixelscale = 0.05
    psf = tel.calc_psf(monochromatic=1e-6, fov_pixels=64)

    tel.optsys.display(what="intensity", ax=axes[0, col], title=label, colorbar=False)
    psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False)
    axes[0, col].set_xlabel("")

axes[0, 0].set_ylabel("Pupil")
axes[1, 0].set_ylabel("PSF")
plt.suptitle("Special aperture geometries", fontsize=13)
plt.show()
special_versions = { "0": "No spider\n(ideal)", "1_3": "3-strut\n(classic)", "1_4": "4-strut\n(cross)", "3": "4-strut\n(v3)", "5": "4-strut\n(tangent offset)", "6": "8-strut\n(star shape)", } fig, axes = plt.subplots(2, len(special_versions), figsize=(18, 6)) plt.subplots_adjust(hspace=0.15, wspace=0.15) for col, (ver, label) in enumerate(special_versions.items()): tel = psfcraft.NewtonianTelescope(version=ver) tel.pixelscale = 0.05 psf = tel.calc_psf(monochromatic=1e-6, fov_pixels=64) tel.optsys.display(what="intensity", ax=axes[0, col], title=label, colorbar=False) psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False) axes[0, col].set_xlabel("") axes[0, 0].set_ylabel("Pupil") axes[1, 0].set_ylabel("PSF") plt.suptitle("Special aperture geometries", fontsize=13) plt.show()
No description has been provided for this image

3. Effect of the Secondary Obstruction Ratio¶

The obstruction ratio $\epsilon = R_2 / R_1$ affects the height of the PSF core and the prominence of the Airy rings. A larger secondary suppresses the core and redistributes power into the rings.

In [4]:
Copied!
primary_radius = 0.5  # fixed
secondary_ratios = [0.0, 0.1, 0.2, 0.33, 0.5]  # ε = R2/R1

fig, axes = plt.subplots(2, len(secondary_ratios), figsize=(15, 6))
plt.subplots_adjust(hspace=0.15, wspace=0.15)

for col, epsilon in enumerate(secondary_ratios):
    secondary_r = epsilon * primary_radius
    # version '0' = no struts, version '1_0' = secondary with no struts
    version = "0" if epsilon == 0.0 else "1_0"
    tel = psfcraft.NewtonianTelescope(
        version=version,
        primary_radius=primary_radius,
        secondary_radius=secondary_r if epsilon > 0 else 0.001,
    )
    tel.pixelscale = 0.05
    psf = tel.calc_psf(monochromatic=1e-6, fov_pixels=64)

    tel.optsys.display(what="intensity", ax=axes[0, col],
                       title=f"ε = {epsilon:.2f}", colorbar=False)
    psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False)
    axes[0, col].set_xlabel("")

axes[0, 0].set_ylabel("Pupil")
axes[1, 0].set_ylabel("PSF")
plt.suptitle("Effect of secondary obstruction ratio  ε = R₂/R₁", fontsize=13)
plt.show()
primary_radius = 0.5 # fixed secondary_ratios = [0.0, 0.1, 0.2, 0.33, 0.5] # ε = R2/R1 fig, axes = plt.subplots(2, len(secondary_ratios), figsize=(15, 6)) plt.subplots_adjust(hspace=0.15, wspace=0.15) for col, epsilon in enumerate(secondary_ratios): secondary_r = epsilon * primary_radius # version '0' = no struts, version '1_0' = secondary with no struts version = "0" if epsilon == 0.0 else "1_0" tel = psfcraft.NewtonianTelescope( version=version, primary_radius=primary_radius, secondary_radius=secondary_r if epsilon > 0 else 0.001, ) tel.pixelscale = 0.05 psf = tel.calc_psf(monochromatic=1e-6, fov_pixels=64) tel.optsys.display(what="intensity", ax=axes[0, col], title=f"ε = {epsilon:.2f}", colorbar=False) psfcraft.display_psf(psf, ax=axes[1, col], title="", colorbar=False) axes[0, col].set_xlabel("") axes[0, 0].set_ylabel("Pupil") axes[1, 0].set_ylabel("PSF") plt.suptitle("Effect of secondary obstruction ratio ε = R₂/R₁", fontsize=13) plt.show()
No description has been provided for this image

Key Takeaways¶

  • The version parameter controls the spider architecture; use '1_N' for $N$ evenly-spaced struts.
  • Each strut generates two diffraction spikes; N struts → 2N spikes (or N if $N$ is even, due to symmetry pairing).
  • The version='0' aperture is the ideal reference: no secondary, no spikes.
  • Increasing the secondary obstruction ratio redistributes PSF energy from the core to the rings.

Next: 04_wavefront_aberrations.ipynb — model optical aberrations with Zernike polynomials.

Previous Next

Copyright © CNRS 2022–2026 — PSFCraft is part of the ANR DISPERS project (ANR-22-CE46-0009). Maintained by Lucas Sauniere, William Gillard, and Julien Zoubian.

Built with MkDocs using a theme provided by Read the Docs.
« Previous Next »