Operator Calculus, Co.
Browse Studio documentation
On this page

Lifecycle at a Glance ✨

The simulation context (ctx) is the core object managing state, operators, fields, and execution. Use the following functions to create and control contexts:

Function Parameters Description
create [config] Create a new simulation context with optional configuration.
context_init_with_universe [config], [universe] Create a new context with an explicit universe (q/K/epsilon/poles).
step ctx Advance the simulation by one timestep.
on_step ctx, fn Register a callback to execute after each step (accepts nil to clear).
run ctx, [config] Start a scheduling loop (GUI mode) with optional config.
pause ctx Pause the scheduling loop.
resume ctx Resume a paused scheduling loop.
shutdown ctx Shutdown the simulation context, releasing resources.

Quick Setup 💡

  1. Create & configure — call create() with optional config, then set your timestep.
  2. Add state — allocate fields and operators, then attach an integrator with set_integrator.
  3. Drive the loop — step manually with step or hand off to a scheduler via run. Use on_step for lightweight instrumentation.

Configuration Keys 🔑

Top-level Config

Key Type Notes
worker_count integer Number of worker threads; defaults to the runtime’s autodetected value.
enable_logging boolean Enable the async scheduler logger; uses log_path when set.
enable_profiling boolean Enable per-operator profiling counters and snapshots.
enable_timestep_heuristics boolean Enable adaptive timestep heuristics and timestep_decision.
frame_time_budget_ms double Soft budget for per-frame work; used by schedulers that honor frame pacing.
base_seed integer Seed for context RNG streams (noise, stochastic integrators, scripted RNG).
backend string Backend request: auto, cpu, cuda, or metal.
backend_fallback string Policy when the requested backend is unavailable: allow, warn, or fail.
log_path string Optional log file path for the async logger (safe relative path only).
continuity_override table Optional continuity guard applied across operators (see below).

Note: Implicit Global Config

If you call create() or context_init_with_universe() without an explicit config table, Oakfield will also look for a global sim_config table and apply the same keys from there.

Continuity Override

Key Type Allowed Values Notes
enabled boolean true/false Toggles continuity guards globally.
mode string none, strict, clamped, limited Modes mirror operator schema defaults; strict preserves analytic continuity while clamped/limited bound singularities.
clamp_min / clamp_max double any Hard bounds applied when clamped or limited.
tolerance double any Blending window for limited mode.
boundary string neumann, dirichlet, periodic, reflective Inherits boundary policy definitions from operator schemas.
spacing double[] positive Optional array of spatial spacings; overrides default operator spacing.
dx double positive Optional scalar spacing override; dx is used when spacing is not an array.

Note: Continuity Override Behavior

Continuity overrides are applied immediately after creation via context_set_continuity_override, so the first operator you register already reflects the chosen policy.

Runtime Continuity Controls

You can update (or disable) the global continuity override at any time:

-- enable/replace override
ooc.context_set_continuity_override(ctx, true, {
    mode = "limited",
    tolerance = 1.0e-3,
    boundary = "neumann",
})

-- disable override (keeps defaults from schemas)
ooc.context_set_continuity_override(ctx, false)

Note: Scheduler-backed Execution

worker_count, enable_logging, enable_profiling, enable_timestep_heuristics, frame_time_budget_ms, and log_path primarily affect scheduler-backed execution.

That includes run(ctx, ...), and also step(ctx) when the runtime chooses the scheduler path internally (for example: multi-worker execution with an attached integrator and no Lua operator callbacks). When step falls back to the legacy single-threaded path, those knobs have little or no effect.

Caution: Log Path Safety

log_path must be a safe relative path (no absolute paths or .. traversal). Invalid paths raise an error when the config is applied.

Example 📖


local ctx = ooc.create({
    worker_count = 2,
    log_path = "logs/run.log",
    continuity_override = {
        enabled = true,
        mode = "clamped",
        clamp_min = -4.0,
        clamp_max = 4.0,
        boundary = "neumann",
    }
})

ooc.set_timestep(ctx, 0.025)

local field = ooc.add_field(ctx, {768}, {
    type = "complex_double", 
    fill = {0.0, 0.0}
})

local integrator = ooc.create_context_integrator(ctx, "rkf45", {
    initial_dt = 0.025,
    tolerance = 1.0e-3,
    safety = 0.9
})

ooc.set_integrator(ctx, integrator)

ooc.on_step(ctx, function(c)
    local t = ooc.get_time(c)
    ooc.log("step %d at t=%.3f", ooc.get_step_index(c), t)
end)

ooc.step(ctx)   -- single-step drive
ooc.shutdown(ctx)

Tip: When to use run

run, pause, and resume are thin scheduling helpers intended for GUI/evented drivers.

For scriptable batch jobs (as in lua/scripts/cli/*), explicit step loops keep control flow and logging predictable.

Universe & Seeding (Advanced) 🧬

Most scripts should use create. If you need to control the special-function universe at context construction time, use:

context_init_with_universe([config], [universe]) -> ctx

The function accepts either order (config/universe are auto-detected by keys). universe may include:

  • q (number)
  • K (integer, non-negative)
  • epsilon (number)
  • sieve_sigma (number)
  • poles (array of {x,y,z,residue,type}; type is "digamma"|"trigamma"|"tetragamma")

You can read the normalized base seed used for deterministic RNG streams with:

context_seed(ctx) -> integer

Representation & Time Model 🧭

Representation mode governs determinism guarantees and what kinds of operators/kernels are allowed:

context_set_representation_mode(ctx, mode)
context_representation_mode(ctx) -> mode_value, mode_name

mode accepts strict, relaxed, or exploration.

Common aliases are also accepted:

  • explore / experimental for exploration

Time model controls whether dt refinement is treated as convergent (continuous flow) or as part of a discrete map:

context_set_time_model(ctx, "continuous" | "map")

Aliases:

  • flow for continuous
  • discrete for map

Backend Escape Hatch 🔌

Most scripts should use the backend / backend_fallback config keys. For lower-level host integration, the context also exposes its raw backend pointer:

context_backend(ctx) -> lightuserdata|nil
context_set_backend(ctx, backend_ptr_or_nil)

Passing nil detaches any explicitly assigned backend. This is primarily for embedders or tooling that already owns backend instances.

Drift Sandbox 🧪

Drift mode is a “no side effects” evaluation mode used by tooling to probe operators without updating continuity counters/warp masks. Some operators also implement optional save/restore hooks so state is reverted.

context_begin_drift(ctx)
context_end_drift(ctx)
context_in_drift(ctx) -> boolean

Context Logger 🧾

Attach a per-context logger callback (distinct from the scheduler’s async logger):

context_set_logger(ctx, fn_or_nil)

fn_or_nil is called as fn(level_int, message_string).

Emit warnings through the context logger (falls back to stderr if no logger is set):

context_log_warning(ctx, fmt, ...)

Determinism & Kernel Gating 🧷

These helpers let tooling decide whether a kernel-backed operator is allowed given the current backend and representation policy:

context_allows_determinism(ctx, flags) -> boolean
context_kernel_allowed(ctx, required_features, determinism_flags) -> boolean
context_kernel_allowed_mode(ctx, mode, required_features, determinism_flags) -> boolean

flags / determinism_flags can be an integer bitmask or a table of strings: "pure_time", "rewind_safe", "no_stateful_nodes", "deterministic_rng_only".

Memory Limits & Scratch Accounting 🧠

Contexts track memory usage for fields and scratch allocations and enforce configurable limits:

context_set_memory_limits(ctx, limits_table)
context_get_memory_limits(ctx) -> limits_table
context_memory_usage(ctx) -> {fields, scratch, total}
context_check_field_limits(ctx, element_count, field_bytes) -> ok, result_code
context_reserve_scratch(ctx, bytes) -> ok, result_code
context_release_scratch(ctx, bytes)

limits_table keys:

  • max_field_elements
  • max_field_bytes
  • max_fields
  • max_total_field_bytes
  • max_scratch_bytes_per_operator

Diagnostics & Special-Function Flush 🩺

Read the context diagnostics snapshot (fault counts, representation adjustments, etc.):

context_diagnostics(ctx) -> table|nil
context_diagnostics_const(ctx) -> table|nil

When compiled with SIM_DIAGNOSTICS, you can flush accumulated special-function diagnostics into the context:

context_flush_special_diagnostics(ctx)

If diagnostics are disabled, the Lua binding returns false, "SIM_DIAGNOSTICS disabled".

Manual Operator Application 🧰

Apply a single operator immediately (bypassing step/integrators). Useful for debugging or tooling:

context_apply_operator(ctx, operator_or_index) -> ok, result_code