Skip to content
Oakfield Operator Calculus Function Reference Site

Flux Lens

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)

Given a field with spectral coefficients u_hat(k):

  1. Ensure both representations are available (u and u_hat) via FFT/IFFT as needed.
  2. Form u2 = u * u, FFT to u2_hat.
  3. Compute a nonlinear term:
N_hat(k) = -0.5 * i * k * u2_hat(k)
  1. Per-mode work:
S(k) = Re( conj(u_hat(k)) * N_hat(k) )
  1. Cumulative flux Pi(|k|) is the running sum of S(k) ordered by increasing |k|.

If use_dealias is enabled, modes above 2/3 * max_k are zeroed before nonlinear products.

  1. Enable the lens with sim_flux_lens_set_enabled(ctx, true).
  2. Pick a field (optional) with sim_flux_lens_set_field_index.
  3. Read scalars from ooc.sim_flux_lens(ctx) and arrays from lens:buckets() / lens:band().
local lens = ooc.sim_flux_lens(ctx)

The returned object is a cached snapshot (refreshed on access). Use lens:refresh() to force an update.

FieldTypeNotes
enabledboolean Lens is enabled; disabled lenses free their buffers.
lockedboolean Freeze band_lo/hi (marks still update).
band_readyboolean band() arrays are available.
validboolean Marks are valid (1D + computed successfully).
field_indexinteger 0-based field index used by the lens.
use_dealiasboolean Apply 2/3 dealiasing before nonlinear products.
update_periodinteger Only recompute when enough accepted steps pass.
last_update_stepinteger Last step where lens refreshed.
band_lo, band_hidouble Current band bounds in |k| units.
target_band_lo, target_band_hidouble Target bounds derived from marks.
kc, k50, k90double Marks derived from bucketed flux/work.
pi_at_kcdouble Pi value at kc.
pi_min, pi_maxdouble Flux range across buckets.
absS_totaldouble Total absolute work (sum of |S|).
max_kdouble length/2 for the current field.
bucket_countinteger Number of |k| buckets currently populated.
band_capacityinteger Length of the band arrays (matches field length).
MethodReturnsDescription
lens:refresh()booleanForces a bridge refresh of the snapshot.
lens:buckets()tableReturns bucket arrays {k, S, absS, pi}.
lens:band()tableReturns {spec, phys} arrays of complex samples.
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_count
local band = lens:band()
-- band.spec[i] and band.phys[i] are complex pairs: {re, im}
-- #band.spec == #band.phys == lens.band_capacity

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 narrower
ooc.sim_flux_lens_shift_center(ctx, shift) -- shift in |k| units

Parameter notes:

  • smoothing is clamped to [0, 1] (higher = slower band motion).
  • min_bandwidth enforces a minimum (band_hi - band_lo) width.
  • scale_width and shift_center operate on the current band when available, otherwise the target band.
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)
end
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)
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-based
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