[1.0.0] - 2026-07-09
Added
- Boiling on the non-periodic engine.
tau_boilnow works with
engine="extrude", not just"spectral". Each step blends the ring buffer
toward a fresh, independently extruded screen (buf = a·buf + √(1−a²)·fresh,
a = exp(−dt/tau)), so temporal decorrelation composes with frozen flow while
the spatial covariance — and hencer0— is preserved and the screen stays
non-periodic. Unlike the spectral engine's per-mode boiling, the extruder
decorrelates every spatial scale at the singletau_boilrate (real space has
no per-mode handle); staying non-periodic costs a modest deficit in the
largest-scale power of the boiled screen, and re-extruding the fresh window
makes a boiling frame markedly costlier than a frozen one.lgs_altitudenow
composes with boiling on the extruder too (the cone acts on readout geometry,
boiling on the stored turbulence). On the GPU, boiling's fresh-screen
extrusion is batched across layers (one pair of matmuls per row instead of a
Python loop per layer per row), a several-fold speedup over the naive
per-layer path. ConstructingAtmosphere(engine="extrude", tau_boil=...)now
raisesExtrudeBoilingPerformanceWarning, noting that this combination is
still markedly slower thanengine="spectral"boiling.
Changed
- Construction is now model/state separated. Immutable validated
configurations define phase-screen, atmosphere, and extrusion inputs before
backend dispatch; mutable runtime objects own only derived geometry, random
streams, spectra, and ring buffers. The two extrusion implementations share
their stencil-preserving ring compaction primitive. - Validation evidence expanded. The CI validation gallery now asserts and
plots finite-screen structure-function ratios at 1–8 pixels and the scalar
zenith-projection laws forr0,theta0, and layer range. - Benchmark evidence is versioned.
bench_suite.py --jsonrecords the
invocation, Git revision, platform, dependency versions, device selection,
timing budget, and batch size with every result. The 1.0.0 reference artifact
is checked in alongside the published tables. Atmosphere.sample()is ~L× faster for shared-outer-scale profiles. It
now draws one aggregate phase screen per distinctL0rather than one per
layer: independent von Kármán screens with the same PSD shape add exactly
(r0_agg^{-5/3} = Σ r0_i^{-5/3}), so summing them is distributionally
identical to summing per-layer draws. A 9-layerparanal-median(uniform
L0)sample()is ~9× faster on both CPU and GPU (measured 9.0×/9.1× at
512², ~30k screens/s on GPU). Layers that shareL0are pooled; a profile
with mixedL0uses one screen per distinct value. Reproducibility note: for
a profile with multiple layers sharing anL0,sample()now consumes a
different RNG stream, so a fixedseedyields a different (but
statistically identical) realisation than before; single-layer-per-L0
profiles are unchanged (the layer's own generator is reused).- Batched multi-direction tomography on the GPU.
opd(directions=[...])
onengine="spectral"(without the LGS cone) now integrates all directions
through one batched inverse FFT and one subharmonic matmul chain instead of a
Python loop, ~1.8× faster at 512² by removing per-direction kernel-launch
latency (bit-identical output). The CPU path keeps its per-direction fused
loop, which is faster there than a batched transform. - Faster spectral LGS cone frames on the GPU. The per-layer cone-zoom
readout in_integrate_lgs(previously ~77% of the frame, a per-layer,
per-tap Python loop) is now a handful oftake_along_axisgathers batched
over all layers and taps: ~3× at 256²/512² (275 → ~830 fps at 512²),
bit-identical output. Gated to the GPU below a working-set threshold — on the
CPU, and for large(L, n, n_screen)working sets on the GPU (≳1024²), the
cache-friendlier per-layer loop is kept.
Full Changelog: 0.2.0...v1.0.0