Operator Calculus, Co.
Browse Studio documentation
On this page

Time & Timestep Management

Set the timestep used by the integrator or direct plan execution with:

set_timestep(ctx, dt)

Read the accumulated simulation time, timestep, and step index with:

get_time(ctx) -> time
get_timestep(ctx) -> dt
get_step_index(ctx) -> step_index

On-step Callbacks

Register a callback to be invoked after each successful step:

on_step(ctx, fn)

Note: Manual Step Hook

on_step is invoked by step after a successful step, including the cases where step internally delegates to the scheduler for a single step. Long-running scheduler loops started with run do not invoke the Lua hook.

Step Metrics

After each successful step, the runtime records metrics including accepted/requested timestep, RMS error, dirty/stable writes, and active warp mask. Access these via step_metrics_latest and step_metrics_history.

step_metrics_latest(ctx) -> metrics
step_metrics_history(ctx, [max_count]) -> metrics_array

The metrics table has the following fields:

Field Type Description
step_index integer The zero-based step counter at the time of the record.
requested_dt double The timestep requested at the start of the step.
accepted_dt double The timestep accepted at the end of the step.
next_dt double The suggested next timestep for adaptive integrators.
rms_error double The RMS error estimate for the step.
step_wall_ns / step_wall_ms integer / double Total wall time for the step.
integrator_wall_ns / integrator_wall_ms integer / double Wall time spent inside integrator work.
operator_wall_ns / operator_wall_ms integer / double Wall time spent in direct operator execution.
dirty_writes integer Number of fields/operators that wrote data this step.
stable_writes integer Number of fields/operators that wrote stable data this step.
active_warp_mask integer Bitmask of active warps during this step.

step_metrics_history(ctx, max_count) is capped internally at SIM_STEP_METRIC_HISTORY; larger requests are truncated to that history size.

Timestep Heuristics Preview

Query the scheduler’s adaptive timestep recommendation (if enabled):

timestep_decision(ctx) -> decision

The decision table includes:

Field Type Description
kind string keep, shrink, or expand.
kind_value integer Numeric enum value for the decision.
suggested_dt double Proposed timestep if the decision is applied.
enabled boolean Whether heuristics are enabled (enable_timestep_heuristics).
available boolean true when a preview decision is available for the current context.

Note: Decision Availability

available is false until the scheduler has recorded step metrics (and when heuristics are disabled or the scheduler is not active).

Integrator Controls 🔄

Attach an integrator to the context with set_integrator, then control its behavior with the following functions:

set_integrator_adaptive(ctx, enabled)
set_integrator_stochastic(ctx, enabled)
set_integrator_stochastic_strength(ctx, strength)
set_integrator_noise(ctx, noise_type)
set_integrator_seed(ctx, seed)
integrator_info(ctx) -> info_table

The info_table returned by integrator_info includes:

Field Type Description
name string The integrator’s name (e.g., “rkf45”).
adaptive_enabled boolean Whether adaptivity is enabled.
enable_stochastic boolean Whether stochastic perturbations are enabled.
stochastic_strength double The strength of stochastic perturbations.
noise string The type of noise applied (e.g., “gaussian”).
random_seed integer The RNG seed currently in use.
min_dt double The minimum allowed timestep for adaptive integrators.
max_dt double The maximum allowed timestep for adaptive integrators.
tolerance double The error tolerance for adaptive integrators.
safety double The safety factor for adaptive timestep selection.
current_dt double The current timestep being used by the integrator.

Reading Clocks & Metrics 🔍

local ok = ooc.step(ctx)
if ok then
    local t = ooc.get_time(ctx)
    local metrics = ooc.step_metrics_latest(ctx)
    if metrics then
        ooc.log("t=%.4f dt=%.4f rms=%.2e writes=%d",
            t, metrics.accepted_dt, metrics.rms_error, metrics.dirty_writes)
    end
end

-- Grab a rolling window (up to SIM_STEP_METRIC_HISTORY)
for _, m in ipairs(ooc.step_metrics_history(ctx, 32)) do
    -- m has the same shape as metrics above
end

sleep(seconds) is available when you need to yield control in GUI/evented loops without shutting down the context.

On-step Hooks for Sweeps/Logging 🪝

local t = 0

ooc.on_step(ctx, function(context)
    t = t + 1
    local s = math.sin(t * 0.000005)
    ooc.operator_param_set(ctx, 1, "wavenumber", s)
end)

Use this for lightweight parameter sweeps or GUI-driven probes. Because the plan is invalidated each step, avoid heavy work inside the callback; keep it to parameter updates or logging.

Typical Drive Loop 🔄

  1. Seed the timestep with set_timestep (or leave the default if your integrator supplies one).
  2. Attach an integrator and optionally enable adaptivity/noise policies.
  3. Step with step, read get_time and metrics, and adjust parameters mid-run if needed.