Metadata-Version: 2.4
Name: psfcraft
Version: 1.0.0
Summary: A Python package to generate PSFs
Author-email: Lucas Sauniere <sauniere@cppm.in2p3.fr>
License: MIT
Project-URL: homepage, https://gitlab.in2p3.fr/sauniere/psfcraft
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: matplotlib
Requires-Dist: h5py
Requires-Dist: tqdm
Requires-Dist: poppy
Requires-Dist: astropy
Requires-Dist: scipy
Provides-Extra: notebooks
Requires-Dist: jupyterlab; extra == "notebooks"
Requires-Dist: ipykernel; extra == "notebooks"
Provides-Extra: docs
Requires-Dist: mkdocs; extra == "docs"
Requires-Dist: mkdocstrings[python]; extra == "docs"
Requires-Dist: mkdocs-jupyter; extra == "docs"
Provides-Extra: dev
Requires-Dist: psfcraft[docs,notebooks]; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# PSFCraft

> **Python library for Point Spread Function (PSF) simulation of Newtonian telescopes**

PSFCraft lets you generate physically accurate PSFs in a few lines of Python — with full
control over aperture geometry, wavefront aberrations (Zernike polynomials), pixel scale,
field of view, and broadband spectral weighting. It is built on top of
[POPPY](https://poppy-optics.readthedocs.io/) and is designed for astronomers, optical
engineers, and anyone building ML datasets from simulated telescope images.

![Zernike PSF line](docs/images/Zernike_PSF_Line.png)

---

## TL;DR

```python
import psfcraft

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

---

## Motivation

Simulating a realistic telescope PSF requires modelling many interacting effects: aperture
shape and spider struts, Zernike wavefront errors, diffraction, and spectral weighting for
broadband sources. PSFCraft wraps the rigorous Fourier-optics engine of POPPY and packages
it into a clean, high-level API tailored to Newtonian/Cassegrain-style telescope geometries
(originally developed for the [Euclid space mission](https://www.euclid-ec.org/)).

---

## Features

- **Ready-made telescope models** — preconfigured Newtonian apertures (circular, with 3/4/5-arm
  spider variants, adjustable secondary obstruction)
- **Zernike wavefront error injection** — pass a coefficient vector (Noll indexing, in metres
  RMS) to simulate any combination of tip, tilt, defocus, astigmatism, coma, trefoil, …
- **Monochromatic and polychromatic PSFs** — flat wavelength or flux-weighted black-body
  spectrum across Y / J / H photometric bands
- **Image quality metrics** — Encircled Energy (EE) curves, EE50/EE80 radii, FWHM
- **Batch generation** — WFE budget design + randomised dataset generation for ML pipelines
- **FITS output** — standard `astropy.io.fits` HDUList, ready for downstream analysis
- **Interactive WebUI** — single-file, no-dependency browser tool for real-time PSF exploration

---

## Installation

**Requirements:** Python ≥ 3.11

### From PyPI

```bash
pip install psfcraft
```

### From source (recommended)

```bash
git clone https://gitlab.in2p3.fr/sauniere/psfcraft.git
cd psfcraft

python -m venv .venv
source .venv/bin/activate

pip install -e .
```

### Minimal install (pip only)

```bash
pip install poppy astropy numpy scipy matplotlib h5py tqdm
pip install -e .
```

### With notebook support

```bash
pip install -e ".[notebooks]"
```

> **Note on `pysynphot`:** PSFCraft no longer requires `pysynphot`. Polychromatic source
> spectra are computed with a built-in Planck-law implementation that has no extra
> dependencies.

---

## Quick Start

```python
import psfcraft
import numpy as np

# --- 1. Perfect PSF (no aberrations) ---
tel = psfcraft.NewtonianTelescope(version="1_3")  # 3-arm spider
tel.pixelscale = 0.1   # arcsec/pixel

psf = tel.calc_psf(monochromatic=1.2e-6, fov_pixels=64)
psfcraft.display_psf(psf)

# --- 2. Inject wavefront aberrations ---
# Noll indexing, coefficients in metres RMS
#            piston tip  tilt  defocus  astig  astig  coma   coma
wfe = np.array([0,    0,    0,    50e-9,   80e-9, 80e-9, 30e-9, 30e-9])
tel.wfe_coefficients = wfe

psf_aberrated = tel.calc_psf(monochromatic=1.2e-6, fov_pixels=64)

# --- 3. Encircled energy ---
psfcraft.display_ee(psf_aberrated)
ee50 = psfcraft.measure_ee(psf_aberrated)(0.15)   # fraction within 0.15 arcsec radius
print(f"EE50 radius ≈ {ee50:.3f} arcsec")
```

---

## Telescope Versions

The `version` string selects the aperture geometry:

| `version` | Geometry |
|-----------|----------|
| `"0"` | Circular aperture only (no secondary, no spider) |
| `"1_3"` | Secondary + 3 evenly spaced spider arms |
| `"1_4"` | Secondary + 4 spider arms |
| `"1_5"` | Secondary + 5 spider arms |
| `"2"` | Euclid-like asymmetric 3-arm spider |
| `"3"` | Secondary obscuration only (no arms) |

---

## Main API

| Function / Class | Description |
|---|---|
| `NewtonianTelescope(version, wfe_coefficients, ...)` | Build a telescope model |
| `tel.calc_psf(monochromatic=λ, fov_pixels=N)` | Compute a monochromatic PSF |
| `tel.calc_psf(source=dict, fov_pixels=N)` | Compute a polychromatic PSF |
| `display_psf(psf, ax, title, ...)` | Plot a PSF on a log-stretch colour map |
| `display_ee(psf)` | Plot Encircled Energy curve |
| `measure_ee(psf)(r)` | Return EE fraction at radius `r` (arcsec) |
| `build_polychromatic_star(T, filter_name, sampling)` | Build a black-body source dict |
| `build_wfe_budget(radial_budget)` | Design a WFE budget for batch generation |
| `utils.generate_coefficients(wfe_budget)` | Draw a random Zernike coefficient vector |

---

## Tutorial Notebooks

Step-by-step notebooks are located in `docs/tutorials/`:

| # | Notebook | Topic |
|---|----------|-------|
| 1 | `01_introduction.ipynb` | What is PSFCraft? 15-line example |
| 2 | `02_getting_started.ipynb` | Configuration, pixel scale, FOV, FITS I/O |
| 3 | `03_aperture_and_pupil.ipynb` | Spider struts, secondary obstruction |
| 4 | `04_wavefront_aberrations.ipynb` | Zernike WFE injection, OPD maps |
| 5 | `05_encircled_energy.ipynb` | EE curves and EE50/EE80 metrics |
| 6 | `06_polychromatic_psf.ipynb` | Broadband PSFs, stellar temperature effects |
| 7 | `07_psf_generation_pipeline.ipynb` | Batch generation, WFE budget, dataset I/O |

Launch them with:
```bash
pip install -e ".[notebooks]"
jupyter lab docs/tutorials/
```

---

## Interactive WebUI

A single-file, zero-dependency browser tool for real-time PSF exploration:

```bash
xdg-open webui/psfcraft-webui.html   # Linux
open webui/psfcraft-webui.html       # macOS
```

**Features:** Zernike sliders, aperture mask, OPD map, split/decomp modes, detector noise
simulation, log/linear scale, multiple colormaps.

See [`docs/webui.md`](docs/webui.md) for the full reference.

---

## Project Structure

```
psfcraft/
├── psfcraft/
│   ├── psfcraft_core.py   # GenericTelescope, NewtonianTelescope classes
│   ├── optics.py          # NewtonianTelescopeAperture (POPPY CompoundAnalyticOptic)
│   ├── utils.py           # display_psf, display_ee, build_polychromatic_star, PSF_Generator
│   ├── constants.py       # Filter cuts (Y/J/H), detector params, stellar types
│   └── simple_gen.py      # Entry point for batch PSF dataset generation
├── docs/tutorials/        # Step-by-step Jupyter notebooks
├── webui/                 # Single-file interactive PSF viewer
├── scripts/               # Standalone generation scripts (Euclid, SPIE, …)
└── pyproject.toml
```

---

## Documentation

Full documentation (API reference + rendered notebooks) is built with MkDocs:

```bash
pip install -e ".[docs]"
mkdocs serve       # live-reloading local server
make build         # static build → site/
```

---

## Contributing

Contributions are welcome. Please open an issue or merge request on
[GitLab](https://gitlab.in2p3.fr/sauniere/psfcraft).

- Follow the existing code style (no linter enforced yet)
- Add or update the relevant tutorial notebook if your change affects the public API
- Keep `pyproject.toml` dependencies minimal

---

## License

MIT — see [`LICENSE`](LICENSE) for details.

---

## AI Usage

GitHub Copilot (powered by Claude) was used during the development of PSFCraft as an AI
coding assistant for the following tasks:

- **Code assistance** — autocompletion, boilerplate generation, and refactoring suggestions
- **Code review and quality improvement** — identifying issues, enforcing consistency, and improving robustness
- **Documentation** — completing and improving docstrings, tutorial notebooks, and the API reference
- **WebUI development** — assisting with the single-file interactive PSF viewer

All AI-generated contributions were produced under the direct supervision and explicit
validation of the authors. No AI output was integrated without human review.

---

## Acknowledgements

PSFCraft was developed at [Aix Marseille Univ, CNRS/IN2P3, CPPM, Marseille, France](https://www.cppm.in2p3.fr/) as part of the [DISPERS](https://dispers.in2p3.fr/) project.

This work was supported by the **Agence Nationale de la Recherche (ANR)** through the
DISPERS project grant.
