Skip to content
Oakfield Operator Calculus Function Reference Site

Utility Operators

sim_add_coordinate_operator(ctx, field, opts)

Generate index or coordinate-mapped values into a field. Creates spatial basis functions, gradients, positional encodings, or time-varying coordinate masks.

sim_add_coordinate_operator(ctx, field, [options]) -> operator

Returns: Operator handle (userdata)

Index Mode:

For a field with NN elements, the raw coordinate at index ii is simply ii.

Coordinate Modes:

  • axis: c=xjc = x_j or c=yjc = y_j depending on coord_axis
  • angle: c=xcos(θ)+ysin(θ)c = x \cos(\theta) + y \sin(\theta) where θ\theta is coord_angle
  • radial: c=(xcxvxt)2+(ycyvyt)2c = \sqrt{(x - c_x - v_x t)^2 + (y - c_y - v_y t)^2} with optional moving center
  • separable: c=f(x)g(y)c = f(x) \circ g(y) where \circ is multiply or add

Normalization:

cnorm={cnonec/(N1)unit[0,1]c/(N1)0.5centered[0.5,0.5]2c/(N1)1signed[1,1]c_{\text{norm}} = \begin{cases} c & \text{none} \\ c / (N-1) & \text{unit} \in [0, 1] \\ c / (N-1) - 0.5 & \text{centered} \in [-0.5, 0.5] \\ 2c / (N-1) - 1 & \text{signed} \in [-1, 1] \end{cases}

Final output:

outi=gaincnorm+bias\text{out}_i = \text{gain} \cdot c_{\text{norm}} + \text{bias}

Core Parameters:

ParameterTypeDefaultRangeDescription
modeenum "index"index, coordSource mapping mode
normalizeenum "none"see belowNormalization applied to coordinate
gaindouble 1.0unboundedMultiplicative gain after normalization
biasdouble 0.0unboundedAdditive bias after gain
time_offsetdouble 0.0unboundedTime offset for coordinate evaluation
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Normalization options: none, unit, centered, signed

Coordinate Mode Parameters (when mode = "coord"):

ParameterTypeDefaultRangeDescription
coord_modeenum "axis"see belowCoordinate mapping mode
coord_axisenum "x"x, yAxis for axis-mode
coord_combineenum "multiply"multiply, addCombine rule for separable mode
coord_angledouble 0.0radiansAngle for angle-mode

Coordinate mode options: axis, angle, radial, separable

Spatial Parameters:

ParameterTypeDefaultRangeDescription
origin_xdouble 0.0unboundedX origin (mapped to index 0)
origin_ydouble 0.0unboundedY origin (mapped to index 0)
spacing_xdouble 1.0>0Grid spacing in X direction
spacing_ydouble 1.0>0Grid spacing in Y direction

Radial Mode Parameters:

ParameterTypeDefaultRangeDescription
coord_center_xdouble 0.0unboundedRadial center X position
coord_center_ydouble 0.0unboundedRadial center Y position
coord_velocity_xdouble 0.0unboundedRadial center X velocity (units/s)
coord_velocity_ydouble 0.0unboundedRadial center Y velocity (units/s)
  • Operates elementwise; no boundary handling required
  • Output field is overwritten (or accumulated into) regardless of initial state
  • For 2D fields, assumes row-major layout with spacing_x and spacing_y
  • Unconditionally stable (pure assignment operation)
  • Time-varying radial coordinates depend on simulation time; ensure time_offset aligns with your integration scheme
  • No temporal accumulation issues when accumulate = false
  • Lightweight pointwise operation; O(N) complexity
  • Radial mode requires square root per element
  • Moving radial center (nonzero velocity) uses simulation time
  • Consider precomputing static coordinates once rather than every timestep
-- Linear index ramp normalized to [0, 1]
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "index",
normalize = "unit"
})
-- Signed index ramp [-1, 1]
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "index",
normalize = "signed",
gain = 2.0,
bias = 0.5
})
-- X-axis coordinate with custom spacing
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "axis",
coord_axis = "x",
spacing_x = 0.1,
origin_x = -5.0,
normalize = "none"
})
-- Radial distance from center of 256x256 field
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "radial",
coord_center_x = 128,
coord_center_y = 128,
normalize = "signed"
})
-- Moving radial center for tracking applications
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "radial",
coord_center_x = 64,
coord_center_y = 64,
coord_velocity_x = 10.0, -- moves right at 10 units/s
coord_velocity_y = 0.0
})
-- Angled gradient at 45 degrees
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "angle",
coord_angle = math.pi / 4,
gain = 2.0,
bias = -1.0
})
-- Separable X*Y coordinate product
ooc.sim_add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "separable",
coord_combine = "multiply",
normalize = "signed"
})

sim_add_phase_rotate_operator(ctx, field, opts)

Apply a uniform complex rotation to all field samples. Fundamental operation for frequency shifting, demodulation, and rotating reference frames.

sim_add_phase_rotate_operator(ctx, field, [options]) -> operator

Returns: Operator handle (userdata)

For each complex sample ziz_i:

zizieiωΔtz_i \leftarrow z_i \cdot e^{i \omega \Delta t}

where ω\omega is the phase_rate in radians per second.

Equivalent polar form:

zieiϕiziei(ϕi+ωΔt)|z_i| e^{i\phi_i} \leftarrow |z_i| e^{i(\phi_i + \omega \Delta t)}

The magnitude is preserved; only the phase advances by ωΔt\omega \Delta t per timestep.

Cartesian implementation:

(Re(z)Im(z))(cos(ωΔt)sin(ωΔt)sin(ωΔt)cos(ωΔt))(Re(z)Im(z))\begin{pmatrix} \text{Re}(z) \\ \text{Im}(z) \end{pmatrix} \leftarrow \begin{pmatrix} \cos(\omega \Delta t) & -\sin(\omega \Delta t) \\ \sin(\omega \Delta t) & \cos(\omega \Delta t) \end{pmatrix} \begin{pmatrix} \text{Re}(z) \\ \text{Im}(z) \end{pmatrix}
ParameterTypeDefaultRangeDescription
phase_ratedouble unboundedAngular rotation rate ω\omega in rad/s (required)
  • Operates elementwise; no boundary handling required
  • Complex fields only: Real fields are unaffected (no imaginary component to rotate)
  • Initial phase determines starting point; rotation is relative
  • Unconditionally stable: Rotation is unitary (magnitude-preserving)
  • Energy-conserving: zi|z_i| remains constant
  • Phase accumulates linearly with time: ϕ(t)=ϕ0+ωt\phi(t) = \phi_0 + \omega t
  • For very large ωΔt|\omega \Delta t|, consider reducing timestep to avoid phase aliasing
  • Requires one sin/cos evaluation per timestep (not per element)
  • Two multiplications and one addition per complex element
  • Very efficient; rotation matrix is computed once per step
  • No memory allocation beyond the field itself
-- Rotate at 1 Hz (2π rad/s)
ooc.sim_add_phase_rotate_operator(ctx, carrier, {
phase_rate = 2 * math.pi
})
-- Negative rotation (clockwise in complex plane)
ooc.sim_add_phase_rotate_operator(ctx, signal, {
phase_rate = -1.0
})
-- Demodulation: shift carrier frequency to baseband
local carrier_freq = 440.0 -- Hz
ooc.sim_add_phase_rotate_operator(ctx, modulated_signal, {
phase_rate = -2 * math.pi * carrier_freq
})
-- Slow phase drift for visualization
ooc.sim_add_phase_rotate_operator(ctx, wave, {
phase_rate = 0.1
})
-- Rotating reference frame for oscillator analysis
local omega_0 = 100.0 -- natural frequency
ooc.sim_add_phase_rotate_operator(ctx, oscillator_state, {
phase_rate = -omega_0 -- move to co-rotating frame
})

sim_add_copy_operator(ctx, input, output, opts)

Copy input field samples to an output field, with optional accumulation. Fundamental building block for field routing, buffering, and multi-stage pipelines.

sim_add_copy_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

For each sample index ii:

outi={outi+inputiΔtif accumulate and scale_by_dtouti+inputiif accumulate onlyinputiotherwise (overwrite)\text{out}_i = \begin{cases} \text{out}_i + \text{input}_i \cdot \Delta t & \text{if accumulate and scale\_by\_dt} \\ \text{out}_i + \text{input}_i & \text{if accumulate only} \\ \text{input}_i & \text{otherwise (overwrite)} \end{cases}
ParameterTypeDefaultDescription
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt
  • No boundary handling required; operates elementwise
  • Input and output fields must have compatible shapes and element counts
  • Complex fields are copied component-wise (both real and imaginary parts)
  • Pure copy is unconditionally stable
  • When accumulating, ensure time integration is handled correctly to avoid unbounded growth
  • Minimal computational overhead; essentially a memory copy
  • When accumulate = false, may be optimized to a direct memcpy
  • Useful for double-buffering or creating field snapshots before destructive operations
-- Simple field copy
ooc.sim_add_copy_operator(ctx, source, destination)
-- Accumulate input into running sum (scaled by dt)
ooc.sim_add_copy_operator(ctx, flux, integral, {
accumulate = true,
scale_by_dt = true
})
-- Accumulate without dt scaling (raw summation)
ooc.sim_add_copy_operator(ctx, sample, buffer, {
accumulate = true,
scale_by_dt = false
})
-- Double-buffering pattern
local u_prev = ooc.sim_add_field(ctx, {N}, { fill = 0.0 })
ooc.sim_add_copy_operator(ctx, u, u_prev) -- snapshot before update
-- ... operators that modify u ...

sim_add_scale_operator(ctx, input, output, opts)

Scale input field samples by a constant factor. Essential for applying gain, normalization, or unit conversion.

sim_add_scale_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

For each sample index ii:

outi={outi+sinputiΔtif accumulate and scale_by_dtouti+sinputiif accumulate onlysinputiotherwise (overwrite)\text{out}_i = \begin{cases} \text{out}_i + s \cdot \text{input}_i \cdot \Delta t & \text{if accumulate and scale\_by\_dt} \\ \text{out}_i + s \cdot \text{input}_i & \text{if accumulate only} \\ s \cdot \text{input}_i & \text{otherwise (overwrite)} \end{cases}

where ss is the scale parameter.

ParameterTypeDefaultDescription
scaledouble 1.0Multiplicative scale factor
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt
  • No boundary handling required; operates elementwise
  • Input and output fields must have compatible shapes
  • Complex fields: scale applies to both real and imaginary parts
  • Unconditionally stable for any finite scale value
  • scale > 1.0 amplifies; scale < 1.0 attenuates
  • scale = 0.0 zeros the output (prefer sim_add_zero_field_operator for clarity)
  • Negative scales invert the sign of the field
  • Single multiplication per element; highly efficient
  • Can operate in-place when input == output
  • Consider fusing with other operators when possible to reduce memory bandwidth
-- Apply gain of 0.5
ooc.sim_add_scale_operator(ctx, signal, attenuated, {
scale = 0.5
})
-- Invert field sign
ooc.sim_add_scale_operator(ctx, u, u_neg, {
scale = -1.0
})
-- In-place scaling
ooc.sim_add_scale_operator(ctx, field, field, {
scale = 0.99 -- gentle decay
})
-- Accumulate scaled values (e.g., for weighted averaging)
ooc.sim_add_scale_operator(ctx, sample, weighted_sum, {
scale = weight,
accumulate = true
})
-- Diffusion coefficient application
local D = 0.01
ooc.sim_add_laplacian_operator(ctx, u, lap_u)
ooc.sim_add_scale_operator(ctx, lap_u, du_dt, { scale = D })

sim_add_zero_field_operator(ctx, field)

Overwrite all samples in a field with zeros. Commonly used to reset accumulators, clear buffers, or initialize state at the start of each timestep.

sim_add_zero_field_operator(ctx, field) -> operator

Returns: Operator handle (userdata)

Note: This operator takes no options table—just the context and field.

For each sample index ii:

fieldi=0\text{field}_i = 0

For complex fields, both real and imaginary components are set to zero.

None. The operator simply zeros the specified field.

  • No boundary handling required; all elements are set to zero
  • Operates in-place on the target field
  • Unconditionally stable; always produces zeros
  • Does not depend on timestep or field state
  • Highly optimized; typically compiled to a fast memory-set operation
  • Does not use time (uses_time = false) or timestep (uses_dt = false)
  • Preserves real subspace (complex fields become real zero + imaginary zero)
-- Zero a field at each step (useful for accumulators)
local accumulator = ooc.sim_add_field(ctx, {N}, { fill = 0.0 })
ooc.sim_add_zero_field_operator(ctx, accumulator)
-- ... operators that accumulate into the field ...
-- Reset state before stimulus injection
ooc.sim_add_zero_field_operator(ctx, state)
ooc.sim_add_stimulus_operator(ctx, state, {
type = "stimulus_sine",
amplitude = 1.0,
wavenumber = 0.5,
omega = 0.1
})
-- Clear a buffer in a ping-pong scheme
ooc.sim_add_zero_field_operator(ctx, buffer_b)
ooc.sim_add_copy_operator(ctx, buffer_a, buffer_b, { accumulate = true })