Skip to content
Oakfield Operator Calculus Function Reference Site

Logging & Profiling

log([ctx], [level], fmt, ...)
  • If the first argument is a context, it is ignored for logging but allowed for convenience.
  • level is optional and must be an integer log-level constant such as ooc.LOG_LEVEL_INFO.
  • Remaining arguments follow Lua string.format semantics; the message is formatted before printing.

Examples:

ooc.log("Hello from Lua")
ooc.log(ooc.LOG_LEVEL_DEBUG, "value=%.3f", 1.234)
ooc.log(ctx, ooc.LOG_LEVEL_WARN, "step %d exceeded threshold", step)
ConstantLevelDescription
ooc.LOG_LEVEL_TRACE0Very detailed debug information
ooc.LOG_LEVEL_DEBUG1General debug information
ooc.LOG_LEVEL_INFO2Informational messages
ooc.LOG_LEVEL_WARN3Warning conditions
ooc.LOG_LEVEL_ERROR4Error conditions
ooc.LOG_LEVEL_FATAL5Fatal conditions

String level shorthands are accepted by scheduler_drain_logs, not by log: "trace", "debug", "info", "warn" (or "warning"), "error", "fatal".

seed(seed_value) -> normalized_seed

Seeds Oakfield’s Lua-side RNG stream and returns the normalized seed actually used.

local normalized = ooc.seed(1234)
ooc.log("rng seeded with %d", normalized)
random() -> number -- uniform in [0, 1)
random(upper) -> number|integer
random(lower, upper) -> number|integer

Behavior matches the argument types you pass:

  • random() returns a floating-point number in [0, 1).
  • random(integer_upper) returns an integer in [1, upper].
  • random(number_upper) returns a float in [0, upper).
  • random(integer_lower, integer_upper) returns an integer in [lower, upper].
  • random(number_lower, number_upper) returns a float in [lower, upper].
ooc.seed(42)
local u = ooc.random()
local die = ooc.random(6)
local signed = ooc.random(-1.0, 1.0)
monotonic_time() -> seconds

Returns wall-clock time from a monotonic source in seconds. This is useful for benchmarking and elapsed-time reporting.

local t0 = ooc.monotonic_time()
for i = 1, 1000 do
ooc.step(ctx)
end
local elapsed = ooc.monotonic_time() - t0
ooc.log("elapsed %.6f s", elapsed)
async_logger_create([capacity]) -> logger

Allocates an in-memory async ring-buffer logger and returns a Logger userdata. The optional capacity argument defaults to 256.

Logger methods:

  • logger:log(level, message) -> true

level must be an integer log-level constant. The method raises a Lua error if the record cannot be appended.

Example:

local logger = ooc.async_logger_create()
assert(logger:log(ooc.LOG_LEVEL_INFO, "Async Logger test..."))
assert(logger:log(ooc.LOG_LEVEL_TRACE, string.format("N = %d, dt = %f", N, dt)))

These helpers surface runtime data to Lua. Scheduler profiling requires enable_profiling = true in the config passed to run.

context_metrics(ctx) -> table

Returns a lightweight health snapshot useful for logging and diagnostics: field_count, operator_count, plan_operator_count, plan_valid, backend, step_index, time_seconds, dt, total_bytes. See Introspection for the full reference.

local m = ooc.context_metrics(ctx)
ooc.log("t=%.3f dt=%.4f backend=%s plan_valid=%s",
m.time_seconds, m.dt, m.backend, tostring(m.plan_valid))
profiler_snapshot(ctx) -> table|nil

Returns the last profiled frame snapshot, or nil when no snapshot is available. The table contains:

  • frame_start_ns
  • frame_end_ns
  • total_ns
  • average_operator_ns
local snap = ooc.profiler_snapshot(ctx)
if snap then
ooc.log("frame=%.3f ms avg=%.3f ns", snap.total_ns / 1e6, snap.average_operator_ns)
end
operator_profiler(ctx) -> entries, total_ms
operator_profiler(ctx) -> nil

Returns per-operator timing for the last profiled frame. When available, entries is an array of:

  • name
  • inclusive_ms
  • invocations
  • delta_rms

If no profiled frame is available, the binding returns nil.

local entries, total_ms = ooc.operator_profiler(ctx)
if entries then
ooc.log("operator profiler total=%.4f ms", total_ms)
for _, e in ipairs(entries) do
ooc.log("%-24s %.5f ms", e.name, e.inclusive_ms)
end
end

If the context has zero operators, the binding returns an empty array and 0.0.

These are low-level profiler views intended for tooling and headless scripts:

context_profiler_snapshot(ctx) -> table|nil
context_profiler_counters(ctx) -> counters|nil

context_profiler_counters returns an array of counter tables in plan-index order, each with:

  • inclusive_ns
  • invocations
  • delta_rms_sum
  • delta_sample_count

When the scheduler is running with profiling enabled, you can also query the scheduler’s last frame:

scheduler_profiler_snapshot(ctx) -> table|nil
scheduler_profiler_counters(ctx) -> counters|nil
scheduler_drain_logs(ctx, [min_level], [callback]) -> boolean

Accepted call patterns:

  • scheduler_drain_logs(ctx) drains and discards all pending records
  • scheduler_drain_logs(ctx, "warn") drains only warn and above
  • scheduler_drain_logs(ctx, function(level, message) ... end) drains and invokes the callback
  • scheduler_drain_logs(ctx, "info", function(level, message) ... end) combines filtering and callbacks

The callback receives the numeric log level and the message string. If the callback raises, scheduler_drain_logs raises a Lua error.

local seen = 0
ooc.scheduler_drain_logs(ctx, "info", function(level, message)
seen = seen + 1
ooc.log(ooc.LOG_LEVEL_DEBUG, "scheduler[%d] %s", level, message)
end)
profiler_new(operator_count) -> profiler

Creates a standalone profiler userdata you can drive manually. Methods:

  • profiler:begin_frame()
  • profiler:end_frame()
  • profiler:record_operator(index, duration_ns)
  • profiler:record_operator_delta(index, delta_rms, [sample_count])
  • profiler:snapshot() -> {frame_start_ns, frame_end_ns, total_ns, average_operator_ns}|nil

Example:

local p = ooc.profiler_new(3)
p:begin_frame()
p:record_operator(0, 250000)
p:record_operator_delta(0, 1.0e-4, 4)
p:end_frame()
local snap = p:snapshot()
if snap then
print("manual frame ns", snap.total_ns)
end

These runtime helpers register backend models for Neural Infer / Neural Hybrid workflows and inspect their runtime stats.

register_neural_model(ctx, config) -> model_index

config keys:

  • model_id or id (required)
  • backend
  • default_precision
  • deterministic
  • external_command or command
  • note
  • supported_devices

Accepted backend strings:

  • callback
  • external_process
  • onnx_runtime
  • libtorch

Accepted default_precision strings:

  • default
  • fp32
  • fp64
  • mixed
  • fp16
  • bf16

supported_devices may be:

  • a string such as "cpu,cuda" or "cpu|mps"
  • a Lua array like { "cpu", "cuda" }
  • an integer device mask

From Lua, the practical backends are external_process, onnx_runtime, and libtorch. The callback backend requires a native callback pointer, which the Lua registration helper does not expose.

local model_index = ooc.register_neural_model(ctx, {
model_id = "demo",
backend = "external_process",
command = "/bin/true",
default_precision = "fp32",
supported_devices = { "cpu" },
deterministic = true,
note = "placeholder process-backed model",
})
neural_model_stats(ctx, model_id) -> model_table|nil
neural_model_stats(ctx) -> model_table[]

With a model_id, returns one table or nil if the model is not found. Without a model_id, returns a 1-based array of all registered models.

Each table may include:

  • model_id
  • backend
  • backend_index
  • deterministic
  • default_precision_index
  • external_command
  • note
  • supported_device_mask
  • supported_devices
  • invocation_count
  • success_count
  • failure_count
  • total_elements
  • last_wall_ms
  • total_wall_ms
  • last_step_index
  • last_sim_time
  • last_result
  • last_device
  • last_error
local one = ooc.neural_model_stats(ctx, "demo")
if one then
ooc.log("model=%s backend=%s calls=%d",
one.model_id, one.backend, one.invocation_count)
end
for _, model in ipairs(ooc.neural_model_stats(ctx)) do
ooc.log("%s supports %s", model.model_id, table.concat(model.supported_devices, ","))
end
  • log prepends level tags when not in GUI mode; in GUI mode it emits raw text.
  • Formatting errors raise a Lua error with the reason.
  • Use ooc.LOG_LEVEL_* constants rather than raw integers for clarity.
  • monotonic_time() is preferable to os.clock() for wall-clock benchmarking.
  • seed() and random() control Oakfield’s Lua RNG stream only; integrator RNGs and context RNGs use their own APIs.

Pass these in the table to ooc.run(ctx, config_table):

  • enable_profiling = true to collect scheduler profiler data for profiler_snapshot, operator_profiler, and scheduler_profiler_*
  • enable_logging = true to turn on the scheduler async logger backend
  • enable_timestep_heuristics = false to disable adaptive dt decisions
ooc.run(ctx, {
enable_profiling = true,
enable_logging = true,
enable_timestep_heuristics = true,
})