Skip to content

Non-Periodic Boiling

Latest

Choose a tag to compare

@jacotay7 jacotay7 released this 10 Jul 06:09

[1.0.0] - 2026-07-09

Added

  • Boiling on the non-periodic engine. tau_boil now 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 hence r0 — is preserved and the screen stays
    non-periodic. Unlike the spectral engine's per-mode boiling, the extruder
    decorrelates every spatial scale at the single tau_boil rate (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_altitude now
    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. Constructing Atmosphere(engine="extrude", tau_boil=...) now
    raises ExtrudeBoilingPerformanceWarning, noting that this combination is
    still markedly slower than engine="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 for r0, theta0, and layer range.
  • Benchmark evidence is versioned. bench_suite.py --json records 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 distinct L0 rather 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-layer paranal-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 share L0 are pooled; a profile
    with mixed L0 uses one screen per distinct value. Reproducibility note: for
    a profile with multiple layers sharing an L0, sample() now consumes a
    different RNG stream, so a fixed seed yields 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=[...])
    on engine="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 of take_along_axis gathers 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