Metadata-Version: 2.4
Name: pyturb
Version: 1.0.0
Summary: Fast, GPU-optional atmospheric phase screens for adaptive optics.
Project-URL: Homepage, https://github.com/jacotay7/pyturb
Project-URL: Issues, https://github.com/jacotay7/pyturb/issues
Author-email: Jacob Taylor <jacobataylor7@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: adaptive-optics,astronomy,atmosphere,kolmogorov,phase-screen,turbulence,von-karman
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Provides-Extra: accel
Requires-Dist: numba>=0.58; extra == 'accel'
Provides-Extra: compare
Requires-Dist: aotools; extra == 'compare'
Requires-Dist: hcipy; extra == 'compare'
Requires-Dist: soapy; extra == 'compare'
Provides-Extra: cuda11
Requires-Dist: cupy-cuda11x; extra == 'cuda11'
Provides-Extra: cuda12
Requires-Dist: cupy-cuda12x; extra == 'cuda12'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
Provides-Extra: fits
Requires-Dist: astropy>=5; extra == 'fits'
Provides-Extra: plot
Requires-Dist: matplotlib>=3.5; extra == 'plot'
Provides-Extra: test
Requires-Dist: pytest-cov; extra == 'test'
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# pyturb

[![CI](https://github.com/jacotay7/pyturb/actions/workflows/ci.yml/badge.svg)](https://github.com/jacotay7/pyturb/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/pyturb.svg)](https://pypi.org/project/pyturb/)
[![Python](https://img.shields.io/pypi/pyversions/pyturb.svg)](https://pypi.org/project/pyturb/)
[![Docs](https://img.shields.io/badge/docs-jacotay7.github.io%2Fpyturb-teal.svg)](https://jacotay7.github.io/pyturb/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

**Documentation: [jacotay7.github.io/pyturb](https://jacotay7.github.io/pyturb/)**

**Fast, GPU-optional atmospheric turbulence for adaptive optics.**

<p align="center">
  <img src="examples/keck_showcase.webp" width="503" alt="Animated Keck atmosphere showcase: frozen flow, boiling, and on/off-axis LGS turbulence.">
</p>

`pyturb` generates the optical path differences (OPD) that an adaptive-optics
system sees through the atmosphere: full **layered turbulence** for a
representative sky, with per-layer wind, **frozen-flow time evolution**,
off-axis directions, and standard site profiles. It runs on NumPy by default
and switches to CUDA (via CuPy) with a single argument.

## Install

```bash
pip install pyturb            # CPU (NumPy + SciPy)
pip install pyturb[cuda12]    # + CuPy for CUDA 12.x
pip install pyturb[cuda11]    # + CuPy for CUDA 11.x
pip install pyturb[accel]     # + Numba, faster CPU frozen flow
```

## Quickstart

```python
import pyturb

atm = pyturb.Atmosphere.from_profile(
    "paranal-median", seeing=0.8, zenith_angle=30, diameter=8.0, n=512, seed=1
)
print(atm.r0, atm.theta0, atm.tau0)      # Fried param, isoplanatic angle, tau0

for t, opd in atm.frames(dt=1e-3, steps=2000):
    ...                                   # (512, 512) OPD [m], frozen flow

ensemble = atm.sample(256)                # (256, 512, 512) Monte-Carlo OPDs

atm = pyturb.Atmosphere.from_profile("paranal-median", seeing=0.8, device="gpu")
for t, opd in atm.frames(dt=1e-3, steps=2000):
    ...                                   # cupy arrays, ~3,200 fps at 512^2
```

See **[Quickstart](https://jacotay7.github.io/pyturb/quickstart/)** for
off-axis/tomography, boiling, LGS, non-periodic frozen flow, and the
lower-level `PhaseScreen`/`InfinitePhaseScreen` building blocks; and
**[Concepts](https://jacotay7.github.io/pyturb/concepts/)** for r0, L0, Cn²,
θ0 and τ0 if you're new to AO.

## Benchmarks

Full 9-layer Paranal atmosphere, closed-loop OPD frames/s, on an RTX 5090
(GPU) and a 32-core CPU (`pyturb[accel]`). The exact raw artifact and invocation
are [versioned with the benchmarks](benchmarks/artifacts/v1.0.0-reference.json):

| screen | GPU spectral | GPU extrude | GPU Monte-Carlo screens/s | CPU spectral | CPU extrude |
|---|---|---|---|---|---|
| 256² | 3,219 | 4,489 | 104,981 | 1,014 | 2,571 |
| 512² | 3,133 | 1,733 | 29,789 | 283 | 324 |
| 1024² | 1,492 | 602 | 5,970 | 70 | 62 |

The Monte-Carlo column is `Atmosphere.sample()` — the full 9-layer atmosphere.
Layers that share an outer scale are drawn as one aggregate screen (their PSDs
add exactly), so a uniform-`L0` profile costs one FFT, not nine: `sample()` now
runs at nearly the single-layer `PhaseScreen.generate` rate (30,629 512²
screens/s on the GPU, 108,054 at 256²). All Monte-Carlo figures are batched,
device-resident throughput (a batch of 64 kept on the GPU); a single default
call, or one that copies its result back to the host, is lower. Run
`python -c "import pyturb; pyturb.benchmark()"` on your own machine, or
`python benchmarks/bench_suite.py` for the full per-use-case sweep. A
head-to-head against aotools, soapy and HCIPy lives in
**[Comparison](https://jacotay7.github.io/pyturb/comparison/)**.

## Features

- **Layered `Atmosphere`** — named site profiles (`paranal-median`,
  `mauna-kea`, `keck`, `las-campanas`, HV 5/7, ...) or a custom `Cn²(h)` model,
  summed to pupil OPD with per-layer wind and airmass/zenith scaling.
- **Two frozen-flow engines** — `engine="spectral"` (default): exact
  sub-pixel shift-theorem translation, all layers in one FFT, periodic.
  `engine="extrude"`: Assémat–Wilson row extrusion, unbounded and
  non-periodic. Both use fused CUDA/Numba kernels on the hot path.
- **Off-axis / tomography** — `atm.opd(t, directions=[...])` batches several
  guide-star directions through one call.
- **Boiling** — temporal decorrelation on top of frozen flow (`tau_boil`).
- **LGS cone effect** — finite-range sodium beacon focal anisoplanatism
  (`lgs_altitude`).
- **Chromatic OPD** — achromatic by default; `dispersion="edlen"`/`"ciddor"`
  for the dry-air/water-vapour term.
- **Diagnostics** (`pyturb.analysis`) — Zernike decomposition, Noll (1976)
  mode variances, temporal PSD + power-law fit, angular decorrelation.
- **I/O** — `pyturb.save`/`pyturb.load` for FITS (optional astropy) and
  `.npz`, with provenance metadata.
- **GPU-optional** — every class takes `device="gpu"` (CuPy); the GPU path is
  statistically identical to CPU (validated against the same theory to
  numerical precision). CuPy and NumPy have independent RNG streams, so a given
  `seed` draws a *different* realisation on each backend — the guarantee is
  matched statistics, not a bit-for-bit copy.
- **Validated against theory** — structure function, Zernike spectrum,
  temporal PSD and angular decorrelation checked against analytic predictions
  in CI; see **[Validation](https://jacotay7.github.io/pyturb/validation/)**.

See the **[API reference](https://jacotay7.github.io/pyturb/api/)** for every
public function and class, and
**[Interop](https://jacotay7.github.io/pyturb/interop/)** for recipes with
HCIPy, poppy, and DM-fitting loops.

## License

MIT
