Metadata-Version: 2.4
Name: makewfs
Version: 1.0.0
Summary: Configuration-driven adaptive-optics wavefront sensor image simulation.
Project-URL: Homepage, https://github.com/jacotay7/makewfs
Project-URL: Documentation, https://jacotay7.github.io/makewfs/
Project-URL: Repository, https://github.com/jacotay7/makewfs
Project-URL: Issues, https://github.com/jacotay7/makewfs/issues
Project-URL: Changelog, https://github.com/jacotay7/makewfs/blob/main/CHANGELOG.md
Author-email: Jacob Taylor <jacobataylor7@gmail.com>
License: MIT
License-File: LICENSE
Keywords: adaptive-optics,pyramid,shack-hartmann,wavefront-sensor
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: getframes>=2.1.1
Requires-Dist: numpy>=1.23
Requires-Dist: scipy>=1.10
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
Provides-Extra: examples
Requires-Dist: matplotlib>=3.7; extra == 'examples'
Provides-Extra: gpu
Requires-Dist: cupy-cuda12x>=13.0; extra == 'gpu'
Provides-Extra: interop
Requires-Dist: pyturb>=1.0; extra == 'interop'
Provides-Extra: validation
Requires-Dist: hcipy>=0.6; extra == 'validation'
Description-Content-Type: text/markdown

# makewfs

[![CI](https://github.com/jacotay7/makewfs/actions/workflows/ci.yml/badge.svg)](https://github.com/jacotay7/makewfs/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/makewfs.svg)](https://pypi.org/project/makewfs/)
[![Python](https://img.shields.io/pypi/pyversions/makewfs.svg)](https://pypi.org/project/makewfs/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

`makewfs` turns a configured pupil-plane phase/OPD map into a realistic
adaptive-optics wavefront-sensor image. The supported sensors are
Shack–Hartmann and four-face pyramid sensors.

The package owns the wavefront-sensor optics. It deliberately reuses
[`pyturb`](https://github.com/jacotay7/pyturb) for atmospheric OPD and
[`getframes`](https://github.com/jacotay7/getframes) for detector response and
noise; neither model will be reimplemented here.

> **Status:** stable. `makewfs` 1.0 freezes the configuration-driven
> Shack–Hartmann and four-face pyramid API under
> [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Broadband source
> morphology, wavelength-resolved detector QE, Shack–Hartmann sodium-layer
> elongation, and optional end-to-end CuPy execution are supported. The
> documented mean-altitude LGS OPD approximation remains intentionally distinct
> from future range-resolved turbulence.

- [Complete implementation roadmap](ROADMAP.md)
- [Instructions for implementation agents](AGENTS.md)

The minimal API is:

```python
import makewfs

wfs = makewfs.WavefrontSensor.from_toml("wfs.toml")
frame = wfs.expose(opd_m, seed=0)  # getframes.Frame, data in ADU
```

Install the CPU package from PyPI with:

```bash
python -m pip install makewfs
```

For the optional CUDA 12.x path:

```bash
python -m pip install "makewfs[gpu]"
```

Set `device = "gpu"` under `[numerics]` and pass a CuPy OPD array for the
device-resident path. `frame.data` is then a CuPy ADU array; use
`getframes.to_numpy(frame.data)` only at an intentional host boundary.

The repository includes a runnable starter configuration at
[`examples/configs/shack_hartmann_minimal.toml`](examples/configs/shack_hartmann_minimal.toml).
The corresponding pyramid starter is at
[`examples/configs/pyramid_minimal.toml`](examples/configs/pyramid_minimal.toml).
For an ideal photon-rate map, use `wfs.photon_rate(opd_m)`; for one detector
exposure containing several temporal OPD samples, use
`wfs.expose_integrated(samples)`.

For closed-loop work, construct the sensor once and call `expose()` with each
new residual wavefront. The only per-frame inputs are the wavefront and,
optionally, a detector-noise seed; telescope, source, sensor, sampling, and
camera choices live in the configuration.

## CPU/GPU throughput

Warm end-to-end frame throughput on an AMD Ryzen 9 9950X3D and NVIDIA RTX 5090
includes optics, `getframes` detector noise, truth, and ADU, with device-resident
input/output and no host transfers:

| Workflow | Output | Work samples | CPU (frames/s) | GPU (frames/s) | Speedup |
| --- | ---: | ---: | ---: | ---: | ---: |
| 20x20 SH, float32 | 160x160 | 1 | 143.4 | 2,011.2 | 14.03x |
| 60x60 SH, float64 | 360x360 | 1 | 26.3 | 945.2 | 35.94x |
| Broadband/range-sampled SH | 64x64 | 9 | 99.8 | 557.1 | 5.58x |
| Pyramid, 8-point modulation, float32 | 80x80 | 8 | 668.5 | 1,579.4 | 2.36x |
| Pyramid, 32-point modulation, float64 | 108x108 | 32 | 33.3 | 903.9 | 27.18x |

Higher is better. A tiny unmodulated 54x54 pyramid case remains CPU-faster
(3,570 versus 1,541 frames/s) because GPU launch overhead dominates its small
optical workload. See the [full snapshot](benchmarks/device-results.md),
[raw JSON](benchmarks/device-results.json), and
[benchmark methodology](docs/performance.md#cpu-versus-gpu-reference-throughput).
