Skip to main content

aobasis

Modal basis sets for adaptive-optics deformable mirrors: KL, Zernike, Fourier, Hadamard, zonal and zonal-fast modes, for any actuator geometry.

Every generator takes your actuator positions and returns a modal-to-command matrix (M2C), an (n_actuators, n_modes) NumPy array whose column k is the DM command for mode k. It's ready for a reconstructor, a calibration sequence or a real-time controller such as pyRTC.

Installation

pip install aobasis                 # numpy + scipy only
pip install "aobasis[plot]"         # + matplotlib, for .plot()
pip install "aobasis[fits]"         # + astropy, for save_fits / load_fits
pip install "aobasis[tutorials]"    # + matplotlib, astropy and Jupyter, for the tutorials

aobasis is pure Python (3.8–3.14) and is tested on Linux, x86-64 and aarch64.

GPU (optional). KLBasisGenerator(..., use_gpu=True) builds and diagonalizes the covariance with CuPy. Install the CuPy wheel for your CUDA version, e.g. pip install cupy-cuda12x, or conda install -c conda-forge cupy. Without CuPy it falls back to the CPU with a warning, and the modes are the same either way.

Quick start

import numpy as np
import aobasis

# 1. Actuator positions: (N, 2) in metres, centred, in your DM's actuator order
positions = aobasis.make_circular_actuator_grid(telescope_diameter=10.0, grid_size=20)

# 2. A generator and its physical parameters
kl = aobasis.KLBasisGenerator(positions, fried_parameter=0.16, outer_scale=30.0)

# 3. The M2C: (276, 50), piston-free
m2c = kl.generate(n_modes=50, ignore_piston=True)

# 4. Use, inspect, keep
coefficients = np.zeros(50); coefficients[3] = 1.0  # some of mode 3
commands = m2c @ coefficients                       # put modal coefficients on the DM
c2m = aobasis.command_to_mode_matrix(m2c)           # and back
kl.plot(count=6)                                    # needs aobasis[plot]
kl.save("kl_m2c.npz")                               # modes + parameters + options + eigenvalues

What's in it

Bases

Generator Modes Typical use
KLBasisGenerator Karhunen-Loève modes of Von Kármán (or Kolmogorov, outer_scale=np.inf) turbulence at the actuators, with eigenvalues closed-loop control; turbulence statistics and priors
DMKLBasisGenerator KL modes of the DM surface from its influence functions (double diagonalization) control with a real DM
ZernikeBasisGenerator Noll-normalized Zernikes in Noll, OSA/ANSI or Fringe order; annular Zernikes with obscuration= aberration commands, optical testing, NCPA
FourierBasisGenerator sines and cosines, aliased frequencies skipped frequency response, spatial filtering
HadamardBasisGenerator ±1 patterns, Sylvester or Paley matrices (construction="smallest"), selection="balanced" low-noise interaction-matrix calibration
ZonalBasisGenerator single-actuator pokes simplest calibration
ZonalFastBasisGenerator groups of pokes at least min_distance apart (optimal lattice colourings) calibration in a few frames

Shaping a basis. These options are the same on every generator:

  • ignore_piston=True makes every mode exactly zero-mean;
  • remove="tiptilt" (or any (n_actuators, k) array, e.g. waffle) keeps other modes out;
  • orthonormalize=True Gram-Schmidts the modes in order;
  • normalize="rms" | "l2" | "peak" | "pv" sets the scale.

Unknown options raise TypeError.

Geometry.

  • make_circular_actuator_grid builds a square grid by grid_size or pitch, with actuators on the rim or in cell centres.
  • make_hexagonal_actuator_grid and make_concentric_actuator_grid cover other layouts.
  • All three take a central obstruction and spider arms.
  • positions_from_mask reads a boolean DM map, and any (N, 2) array works too.

Influence functions. fit_to_influence_functions gives least-squares commands whose DM surface matches modes sampled on the pupil. gaussian_influence_functions and make_pupil_points build the inputs.

Using and keeping a basis.

  • command_to_mode_matrix and fit_coefficients give modal coefficients.
  • basis_report (or generator.report()) summarizes rank, conditioning, orthogonality and piston content.
  • save/load (.npz) and save_fits/load_fits store the modes together with the generator's parameters, the generate() options, KL eigenvalues and the aobasis version.

Reproducible. KL modes follow a fixed sign and degenerate-rotation convention: tip along +x, tilt along +y. The same geometry gives the same modes on every machine, CPU or GPU.

Tutorials and examples

tutorials/ holds eight Jupyter walkthroughs; see tutorials/README.md for a guide.

Notebooks
Getting started 01 · Quickstart, 02 · A tour of the bases
Everyday use 03 · Shaping a basis, 04 · Actuator geometry
In depth 05 · KL and turbulence, 06 · Influence functions and DM KL, 07 · Calibration patterns, 08 · Saving and sharing

examples/ holds ready-to-run scripts (each has --help):

python examples/make_m2c.py kl --grid-size 20 --n-modes 100 --remove tiptilt -o kl.fits --plot kl.png
python examples/dm_kl.py --n-modes 80 -o dmkl.fits
python examples/calibration.py

Performance

python examples/benchmark.py --grid-sizes 16 32 64 --n-modes 100 --gpu --markdown. Each entry is the best of 3 runs of generate(100) (zonal fast: its full pattern set) on a square grid clipped by a 10 m pupil.

Host: an 80-core Arm Neoverse-N1 server, using 4 cores (OPENBLAS_NUM_THREADS=4), and an NVIDIA RTX A400.

Basis (100 modes) 16×16 (172 acts) 32×32 (740 acts) 64×64 (3096 acts)
KL (CPU) 0.023 s 0.158 s 1.99 s
KL (GPU) 0.026 s 0.161 s 4.41 s
Zernike 0.005 s 0.011 s 0.033 s
Fourier 0.006 s 0.024 s 0.081 s
Hadamard 0.001 s 0.021 s 0.142 s
Zonal <0.001 s <0.001 s 0.002 s
Zonal fast (3-pitch spacing) 0.005 s 0.023 s 0.120 s

Full bases (--n-modes all) on 3096 actuators take 6.3 s for KL, 4.3 s for Zernike, 7.1 s for Fourier and 3.0 s for Hadamard. That includes the rank check.

Some notes on these numbers:

  • KL costs O(N³). For a few modes the CPU uses a partial eigensolver, which is why 100 modes are faster than the full basis.
  • The covariance is evaluated once per distinct actuator separation.
  • The GPU path only pays off on GPUs with strong float64 throughput. The RTX A400 used here has little, and on such cards the CPU is as fast or faster.

Development and testing

git clone https://github.com/jacotay7/aobasis.git
cd aobasis
pip install -e ".[dev]"
pytest

CI runs the test suite on Python 3.8–3.14, on aarch64 and without matplotlib, runs every example script, and executes every tutorial notebook.

CI cannot run the GPU tests: they need CuPy and a CUDA device, and skip without them. Run them locally before changing GPU code:

pip install cupy-cuda12x
pytest -k gpu -rs   # -rs shows a skip reason if no device is found

Contributing

Contributions are welcome. Open an issue to report a bug or propose a feature, or send a pull request with a test for the change and an entry in CHANGELOG.md.

Contact

Jacob Taylor, jacobataylor7@gmail.com

License

MIT; see LICENSE.

Metadata

Release files for aobasis 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aobasis 2.0.0
File Size Uploaded
aobasis-2.0.0.tar.gz 64.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aobasis 2.0.0
File Interpreter ABI Platform
aobasis-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 111.7 kB

Release files / aobasis-2.0.0.tar.gz

Download URL aobasis-2.0.0.tar.gz
Size 64.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a005cd6ccc3281e8af5ff06ee2db7bae41b3d785ab68b6ba97acaa596e765dc3
BLAKE2b-256 checksum
How to use checksums
e758281f637f0d01a774a0c0c2fe668174902b201a215a23afe1a357d996d215
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / aobasis-2.0.0-py3-none-any.whl

Download URL aobasis-2.0.0-py3-none-any.whl
Size 47.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
426c7835a49015c9f0eb7cb5a18f726aa39fe7821784770b874cfc30c0615d76
BLAKE2b-256 checksum
How to use checksums
f4877db4c4987881db55adc67bd3b9e16759e8c1fe9eee1e2033e634ee51e3e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page