Operator Calculus, Co.
Browse Studio documentation

CLI Examples

Examples of more advanced Lua usage patterns, including custom operators for in-plan mutation and lightweight diagnostics.

1) Parameter sweep custom operator

Use a Lua-defined operator to sweep the wavenumber of an existing stimulus over time. This keeps the mutation inside the execution plan instead of trying to reconfigure operators from an observation callback.

local N = 768
local dt = 0.03
local ctx = ooc.create()

ooc.set_timestep(ctx, dt)

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

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

ooc.add_zero_field_operator(ctx, carrier)

local stim_mod_sine = ooc.add_stimulus_operator(ctx, modulator, {
    type = "stimulus_sine",
    amplitude = 0.41,
    wavenumber = 1.0,
    omega = -0.42,
    rotation = math.pi / 2
})

local stim_mod_lines = ooc.add_stimulus_operator(ctx, modulator, {
    type = "stimulus_spectral_lines",
    amplitude = 0.19,
    wavenumber = 1.0,
    omega = 0.01,
    phase = 4.8,
    harmonic_power = 0.94,
    harmonic_count = 2
})

ooc.add_stimulus_operator(ctx, carrier, {
    type = "stimulus_sine",
    amplitude = 1.0,
    wavenumber = 1.0,
    omega = -0.51,
    phase = 0.36,
    spacing_x = 0.14
})

ooc.add_mixer_operator(ctx, modulator, carrier, carrier, {
    mode = "pm",
    lhs_gain = 1.0,
    rhs_gain = 1.8,
    bias = 0.58
})

local integrator = ooc.create_context_integrator(ctx, "rkf45", {
    initial_dt = dt
})
ooc.set_integrator(ctx, integrator)

local tick = 0
local sweep_rate = 0.0005

ooc.add_operator(ctx, "sweep_op", function(context)
    tick = tick + 1
    local sweep = math.sin(tick * sweep_rate)

    ooc.operator_param_set(context, stim_mod_sine, "wavenumber", sweep)
    ooc.operator_param_set(context, stim_mod_lines, "wavenumber", sweep)

    return true
end)

return ctx

2) Lua-defined diagnostics operator

Defines a custom operator that logs the most recent step metrics every 50 ticks while the simulation is running.

local ctx = ooc.create()
local dt = 0.02

ooc.set_timestep(ctx, dt)

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

ooc.add_stimulus_operator(ctx, field, {
    type = "stimulus_sine",
    amplitude = 0.2,
    wavenumber = 0.5,
    omega = 0.15,
    scale_by_dt = true
})

local integrator = ooc.create_context_integrator(ctx, "rk4", {
    initial_dt = dt
})
ooc.set_integrator(ctx, integrator)

local tick = 0

ooc.add_operator(ctx, "tick_counter", function(context)
    tick = tick + 1

    if tick % 50 == 0 then
        local metrics = ooc.step_metrics_latest(context)
        if metrics then
            ooc.log("step=%d dt=%.6f accepted=%s dirty=%d",
                tick,
                metrics.accepted_dt,
                tostring(metrics.accepted),
                metrics.dirty_writes)
        end
    end

    return true
end)

return ctx