Flux Lens
🧠 What It Is
Section titled “🧠 What It Is”The Flux Lens is a context-level diagnostic that computes an energy-transfer spectrum from a 1D field and uses it to maintain a tapered bandpass reconstruction in both spectral and physical space.
It is designed to answer:
- Where is the dominant spectral transfer happening? (
kc) - How concentrated is the transfer? (
k50,k90) - What does the corresponding band-limited signal look like? (
band_phys,band_spec)
⚙️ Method (Skew-Burgers Flux)
Section titled “⚙️ Method (Skew-Burgers Flux)”Given a field with spectral coefficients u_hat(k):
- Ensure both representations are available (
uandu_hat) via FFT/IFFT as needed. - Form
u2 = u * u, FFT tou2_hat. - Compute a nonlinear term:
N_hat(k) = -0.5 * i * k * u2_hat(k)- Per-mode work:
S(k) = Re( conj(u_hat(k)) * N_hat(k) )- Cumulative flux
Pi(|k|)is the running sum ofS(k)ordered by increasing|k|.
If use_dealias is enabled, modes above 2/3 * max_k are zeroed before nonlinear products.
🚀 Quick Start
Section titled “🚀 Quick Start”- Enable the lens with
sim_flux_lens_set_enabled(ctx, true). - Pick a field (optional) with
sim_flux_lens_set_field_index. - Read scalars from
ooc.sim_flux_lens(ctx)and arrays fromlens:buckets()/lens:band().
🛠️ API
Section titled “🛠️ API”Create Proxy
Section titled “Create Proxy”local lens = ooc.sim_flux_lens(ctx)The returned object is a cached snapshot (refreshed on access). Use lens:refresh() to force an update.
Snapshot Fields (Scalars)
Section titled “Snapshot Fields (Scalars)”| Field | Type | Notes |
|---|---|---|
enabled | boolean | Lens is enabled; disabled lenses free their buffers. |
locked | boolean | Freeze band_lo/hi (marks still update). |
band_ready | boolean | band() arrays are available. |
valid | boolean | Marks are valid (1D + computed successfully). |
field_index | integer | 0-based field index used by the lens. |
use_dealias | boolean | Apply 2/3 dealiasing before nonlinear products. |
update_period | integer | Only recompute when enough accepted steps pass. |
last_update_step | integer | Last step where lens refreshed. |
band_lo, band_hi | double | Current band bounds in |k| units. |
target_band_lo, target_band_hi | double | Target bounds derived from marks. |
kc, k50, k90 | double | Marks derived from bucketed flux/work. |
pi_at_kc | double | Pi value at kc. |
pi_min, pi_max | double | Flux range across buckets. |
absS_total | double | Total absolute work (sum of |S|). |
max_k | double | length/2 for the current field. |
bucket_count | integer | Number of |k| buckets currently populated. |
band_capacity | integer | Length of the band arrays (matches field length). |
Methods
Section titled “Methods”| Method | Returns | Description |
|---|---|---|
lens:refresh() | boolean | Forces a bridge refresh of the snapshot. |
lens:buckets() | table | Returns bucket arrays {k, S, absS, pi}. |
lens:band() | table | Returns {spec, phys} arrays of complex samples. |
📐 Array Shapes
Section titled “📐 Array Shapes”Buckets
Section titled “Buckets”local b = lens:buckets()-- b.k[i], b.S[i], b.absS[i], b.pi[i] are numbers (Lua 1-based)-- #b.k == #b.S == #b.absS == #b.pi == lens.bucket_countlocal band = lens:band()-- band.spec[i] and band.phys[i] are complex pairs: {re, im}-- #band.spec == #band.phys == lens.band_capacity🎛️ Control Helpers
Section titled “🎛️ Control Helpers”These are context-level functions (not methods on the proxy):
ooc.sim_flux_lens_set_enabled(ctx, true|false)ooc.sim_flux_lens_force_refresh(ctx)ooc.sim_flux_lens_set_field_index(ctx, field_index0)
ooc.sim_flux_lens_set_update_period(ctx, steps)ooc.sim_flux_lens_set_smoothing(ctx, smoothing01)ooc.sim_flux_lens_set_min_bandwidth(ctx, min_width)ooc.sim_flux_lens_set_use_dealias(ctx, true|false)ooc.sim_flux_lens_set_locked(ctx, true|false)
ooc.sim_flux_lens_scale_width(ctx, scale) -- >1 wider, <1 narrowerooc.sim_flux_lens_shift_center(ctx, shift) -- shift in |k| unitsParameter notes:
smoothingis clamped to[0, 1](higher = slower band motion).min_bandwidthenforces a minimum(band_hi - band_lo)width.scale_widthandshift_centeroperate on the current band when available, otherwise the target band.
🧪 Examples
Section titled “🧪 Examples”1) Minimal enable + marks readout
Section titled “1) Minimal enable + marks readout”ooc.sim_flux_lens_set_enabled(ctx, true)ooc.sim_flux_lens_set_update_period(ctx, 30)
local lens = ooc.sim_flux_lens(ctx)ooc.sim_step(ctx)
lens:refresh()if lens.valid then ooc.log("kc=%.3f k50=%.3f k90=%.3f", lens.kc, lens.k50, lens.k90)end2) Manual refresh rate + lock band
Section titled “2) Manual refresh rate + lock band”ooc.sim_flux_lens_set_enabled(ctx, true)ooc.sim_flux_lens_set_update_period(ctx, 200) -- slower refresh
-- ... run some steps ...ooc.sim_flux_lens_force_refresh(ctx)
local lens = ooc.sim_flux_lens(ctx)lens:refresh()ooc.sim_flux_lens_set_locked(ctx, true)3) Select which field drives the lens
Section titled “3) Select which field drives the lens”local a = ooc.sim_add_field(ctx, {512}, { type = "complex_double", fill = {0, 0} })local b = ooc.sim_add_field(ctx, {512}, { type = "complex_double", fill = {0, 0} })
ooc.sim_flux_lens_set_enabled(ctx, true)ooc.sim_flux_lens_set_field_index(ctx, b) -- or numeric index; engine uses 0-based4) Bucket export for plotting (CSV-style)
Section titled “4) Bucket export for plotting (CSV-style)”local lens = ooc.sim_flux_lens(ctx)ooc.sim_flux_lens_force_refresh(ctx)lens:refresh()
local b = lens:buckets()for i = 1, #b.k do print(string.format("%g,%g,%g,%g", b.k[i], b.S[i], b.absS[i], b.pi[i]))end