Skip to content
Oakfield Operator Calculus Function Reference Site

Handles & Methods

  • Obtain a handle: add_field at creation, or get_field(ctx, index) to look up an existing field.
  • Inspect: :rank(), :shape(), :components(), :domain(), :value_kind(), and :values() snapshot the field into Lua.
  • Write back: :set_values(table) replaces the field buffer from a Lua table.
  • Statistics: field_stats(ctx, field) for a lazy step-cached proxy; field_stats_compute(field) for an immediate one-shot result; the accumulator API for manual rolling stats.
  • Spectral & phase: field_stats_compute_spectral_view and field_stats_compute_phase_metrics compute frequency-domain and phase-coherence metrics.
  • Phase lock: field_stats_update_phase_lock and the field_stats_{get,set}_phase_config functions control hysteresis-based phase-lock detection.

Field handles expose the following methods. __tostring returns Field(<ptr>) or Field(nil) for debug printing; garbage-collection of the handle does not touch the underlying context storage.

Returns the number of dimensions (tensor rank) as an integer.

local r = field:rank() -- e.g. 2 for a {256, 256} field

Returns a Lua array of extents, one per axis, in declaration order (axis 1 = outermost, axis rank = innermost / fastest-varying).

local s = field:shape() -- e.g. {256, 512}
ooc.log("%d rows × %d cols", s[1], s[2])

Returns 1 for real ("double") fields and 2 for complex ("complex_double") fields.

Returns the field’s representation-domain tag as a string, typically "physical" or "spectral".

ooc.log("domain=%s", field:domain())

Returns the field’s value-kind tag as a string. Canonical outputs are "real", "complex", "complex_real_constraint", and "unknown".

ooc.log("value_kind=%s", field:value_kind())

Materializes the entire field buffer into a Lua table (a copy). Mutating the returned table does not affect the field.

  • Real fields → flat array of numbers, row-major (axis rank varies fastest).
  • Complex fields → array of {re, im} two-element tables.
  • Raises a Lua error if the element format is unsupported.
-- Real snapshot
for i, v in ipairs(field:values()) do
ooc.log("[%d] = %.4f", i, v)
end
-- Complex snapshot
for i, z in ipairs(field:values()) do
ooc.log("[%d] = %.3f + %.3fi", i, z[1], z[2])
end

Writes values from a Lua table back into the field buffer. The table must contain at least as many entries as the field has elements; excess entries are ignored.

  • Real fields → elements must be numbers.
  • Complex fields → each element may be:
    • A bare number → stored as {re = value, im = 0}.
    • A {re, im} two-element table.
  • Returns true on success; raises a Lua error if the table is too short or an element type is wrong.
-- Zero out a real field
local zeros = {}
local n = field:shape()[1] * field:shape()[2]
for i = 1, n do zeros[i] = 0.0 end
field:set_values(zeros)
-- Write complex values
local vals = {}
for i = 1, 1024 do
local x = (i - 1) * 2 * math.pi / 1024
vals[i] = { math.cos(x), math.sin(x) }
end
field:set_values(vals)

Returns the number of fields currently allocated in the context.

Returns a Field handle for the given 0-based index. Raises a Lua error if the index is out of range.

local f = ooc.get_field(ctx, 0) -- first field

Returns a table describing the field at the given 0-based index, or nil if the index is missing.

KeyTypeDescription
indexinteger Field index (mirrors the argument)
rankinteger Number of dimensions
element_countinteger Total number of elements
shapetable Array of per-axis extents
formatstring "real_double" or "complex_double"
format_valueinteger Numeric enum value of format
componentsinteger 1 (real) or 2 (complex)
element_sizeinteger Bytes per element (8 or 16)
byte_sizeinteger Total buffer size in bytes
local info = ooc.field_info(ctx, 0)
ooc.log("format=%s bytes=%d", info.format, info.byte_size)

field_stats(ctx, index_or_field) — Lazy Proxy

Section titled “field_stats(ctx, index_or_field) — Lazy Proxy”

Returns a stats proxy whose values are cached per simulation step. On first access within a step the proxy recomputes stats; subsequent reads within the same step return the cached values.

local stats = ooc.field_stats(ctx, field)
ooc.log("rms=%.4f max=%.4f", stats.rms, stats.max_abs)

Call stats:refresh() to force a recompute regardless of the step cache. It returns true when the refresh succeeds and false if the snapshot could not be updated. Use pairs(stats) to iterate a full snapshot table.

FieldTypeDescription
mean_redouble Mean of real components
mean_imdouble Mean of imaginary components
mean_absdouble Mean of absolute values |z|
rmsdouble Root mean square of real components
var_redouble Variance of real components
var_imdouble Variance of imaginary components
var_absdouble Variance of |z|
max_absdouble Maximum absolute value across all elements
phase_coherencedouble Kuramoto order parameter (0–1); 1 = fully coherent
circularitydouble Complex circularity measure
spectral_entropydouble Shannon entropy of the spectral power distribution
spectral_bandwidthdouble RMS spectral bandwidth
phase_coherence_weighteddouble Amplitude-weighted phase coherence
phase_coherence_emadouble Exponential moving average of phase coherence
phase_coherence_k0double Phase coherence at the dominant wavenumber
phase_sample_countinteger Number of samples contributing to phase estimates
phase_lock_stateinteger Hysteresis lock flag (0 = unlocked, 1 = locked)
phase_regimeinteger Encoded phase-regime classification
countinteger Total element count included in stats
continuity_dirty_opsinteger Number of operators that wrote to the field (dirty) last step
continuity_stable_opsinteger Number of operators that left the field unchanged last step

Immediately computes stats for the given Field handle and returns a plain table (not a proxy). No context or step index is required.

The returned table contains all fields listed above plus mean (the mean of real components, equivalent to mean_re for real fields).

local s = ooc.field_stats_compute(field)
ooc.log("mean=%.4f rms=%.4f max=%.4f", s.mean, s.rms, s.max_abs)

Use this when you need an immediate snapshot outside of a step loop, or when you want to compute stats for a temporarily constructed field.


The accumulator API lets you compute stats incrementally over arbitrary sequences of values — useful for streaming data from external sources or computing stats over a subset of elements.

Creates a new accumulator userdata, or resets an existing one if acc is passed. Returns the accumulator handle.

local acc = ooc.field_stats_accumulator_begin()
-- Or reuse an existing accumulator:
ooc.field_stats_accumulator_begin(acc)

Feeds a single real sample into the accumulator.

ooc.field_stats_accumulate_real(acc, 0.42)

field_stats_accumulate_complex(acc, re, im)

Section titled “field_stats_accumulate_complex(acc, re, im)”

Feeds a single complex sample into the accumulator.

ooc.field_stats_accumulate_complex(acc, re, im)

Finalizes the accumulation and returns a stats table with the computed values.

local result = ooc.field_stats_accumulator_finish(acc)
ooc.log("rms=%.4f count=%d", result.rms, result.count)

Full accumulator example:

local acc = ooc.field_stats_accumulator_begin()
for _, v in ipairs(my_field:values()) do
ooc.field_stats_accumulate_real(acc, v)
end
local s = ooc.field_stats_accumulator_finish(acc)
ooc.log("custom rms=%.4f max=%.4f", s.rms, s.max_abs)

These functions require a pre-computed base stats table (from the proxy or one-shot compute) and update it with additional spectral or phase-coherence metrics.

field_stats_compute_spectral_view(field, stats_table)

Section titled “field_stats_compute_spectral_view(field, stats_table)”

Computes spectral statistics for field and updates stats_table in-place with spectral_entropy, spectral_bandwidth, and related fields.

Returns: ok (boolean ), dominant_k (integer ) — the index of the dominant wavenumber bin. ok is false if the field is not complex or the FFT failed.

local stats = ooc.field_stats_compute(field)
local ok, dom_k = ooc.field_stats_compute_spectral_view(field, stats)
if ok then
ooc.log("spectral_entropy=%.4f dominant_k=%d", stats.spectral_entropy, dom_k)
end

field_stats_compute_phase_metrics(field, stats_table[, config_table[, dominant_k]])

Section titled “field_stats_compute_phase_metrics(field, stats_table[, config_table[, dominant_k]])”

Computes phase-coherence metrics and updates stats_table in-place with phase_coherence, phase_coherence_weighted, phase_coherence_ema, phase_coherence_k0, and phase_sample_count.

ArgumentRequiredDescription
fieldyesField handle or field view
stats_tableyesExisting stats table (updated in-place)
config_tablenoPhase config overrides for this call only (see Phase Configuration)
dominant_knoDominant wavenumber from field_stats_compute_spectral_view; defaults to 0
local stats = ooc.field_stats_compute(field)
local ok, dom_k = ooc.field_stats_compute_spectral_view(field, stats)
ooc.field_stats_compute_phase_metrics(field, stats, nil, dom_k)
ooc.log("phase_coherence=%.4f ema=%.4f", stats.phase_coherence, stats.phase_coherence_ema)

field_stats_update_phase_lock(ctx, index, stats_table)

Section titled “field_stats_update_phase_lock(ctx, index, stats_table)”

Updates the hysteresis-based phase_lock_state field in stats_table for the field at index (0-based), using the persistent lock state stored in the context. Updates stats_table in-place.

ooc.field_stats_update_phase_lock(ctx, 0, stats)
ooc.log("lock=%d regime=%d", stats.phase_lock_state, stats.phase_regime)

The phase coherence system uses a global configuration that controls EMA smoothing, hysteresis thresholds, and amplitude weighting.

Returns the current global phase config as a table.

field_stats_set_phase_config(config_table)

Section titled “field_stats_set_phase_config(config_table)”

Merges the provided table into the global phase config. Unknown keys are ignored.

KeyTypeDescription
abs_thresholddouble Absolute amplitude threshold for phase inclusion
rel_thresholddouble Amplitude threshold relative to max_abs
weightedboolean Weight phase samples by amplitude
lock_ondouble Coherence threshold to enter locked state
lock_offdouble Coherence threshold to exit locked state (hysteresis)
smoothing_constantdouble EMA alpha for phase_coherence_ema (also accepted as smoothing_seconds)
deramp_enabledboolean Enable phase-ramp detrending before coherence computation (also accepted as deramp)
ooc.field_stats_set_phase_config({
lock_on = 0.85,
lock_off = 0.70,
smoothing_constant = 0.05,
weighted = true,
})
local cfg = ooc.field_stats_get_phase_config()
ooc.log("lock_on=%.2f lock_off=%.2f", cfg.lock_on, cfg.lock_off)

Creates a lightweight field view from a Field handle. Field views are accepted by field_stats_compute_spectral_view and field_stats_compute_phase_metrics in place of a full Field handle. Useful when you want to pass a view into a function that expects either type.

local view = ooc.field_view_from_field(field)
local ok, dom_k = ooc.field_stats_compute_spectral_view(view, stats)

The returned table contains data (lightuserdata), count, type, and type_name. Treat it as an opaque bridge object tied to the lifetime of the original field.

Returns a table { index, dirty, stable } for the field at the given 0-based index, reflecting how many operators wrote to (dirty) or left unchanged (stable) the field during the most recent simulation step.

local counts = ooc.field_continuity_counts(ctx, 0)
ooc.log("dirty=%d stable=%d", counts.dirty, counts.stable)

local ctx = ooc.create()
local f = ooc.add_field(ctx, {8, 8}, { type = "complex_double", fill = {1, 0} })
ooc.log("rank=%d shape=%s components=%d",
f:rank(),
table.concat(f:shape(), "×"),
f:components())
local stats = ooc.field_stats(ctx, field)
ooc.log("rms=%.4f max=%.4f coherence=%.4f",
stats.rms, stats.max_abs, stats.phase_coherence)
-- Force refresh if you need current-step values immediately:
stats:refresh()
-- Iterate all fields in a snapshot:
for key, val in pairs(stats) do
ooc.log("%s = %s", key, tostring(val))
end
ooc.step(ctx)
local s = ooc.field_stats_compute(field)
local ok, dom_k = ooc.field_stats_compute_spectral_view(field, s)
if ok then
ooc.field_stats_compute_phase_metrics(field, s, nil, dom_k)
ooc.field_stats_update_phase_lock(ctx, 0, s)
ooc.log("entropy=%.3f coherence=%.3f lock=%d", s.spectral_entropy, s.phase_coherence, s.phase_lock_state)
end