Utility Operators
On this page
Coordinate 📐
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.
Method Signature
add_coordinate_operator(ctx, field, [options]) -> operator
Returns: Operator handle (userdata)
Mathematical Formulation
Index Mode:
For a field with elements, the raw coordinate at index is simply .
Coordinate Modes:
- axis: or depending on
coord_axis - angle: where is
coord_angle - radial: with optional moving center
- separable: where is multiply or add
Normalization:
Final output:
Parameters
Core Parameters:
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
mode |
enum |
"index" |
index, coord |
Source mapping mode |
normalize |
enum |
"none" |
see below | Normalization applied to coordinate |
gain |
double |
1.0 |
unbounded | Multiplicative gain after normalization |
bias |
double |
0.0 |
unbounded | Additive bias after gain |
time_offset |
double |
0.0 |
unbounded | Time offset for coordinate evaluation |
accumulate |
boolean |
false |
— | Add to output instead of overwriting |
scale_by_dt |
boolean |
true |
— | Scale accumulated writes by dt |
Normalization options: none, unit, centered, signed
Coordinate Mode Parameters (when mode = "coord"):
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
coord_mode |
enum |
"axis" |
see below | Coordinate mapping mode |
coord_axis |
enum |
"x" |
x, y |
Axis for axis-mode |
coord_combine |
enum |
"multiply" |
multiply, add |
Combine rule for separable mode |
coord_angle |
double |
0.0 |
radians | Angle for angle-mode |
Coordinate mode options: axis, angle, radial, separable
Spatial Parameters:
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
origin_x |
double |
0.0 |
unbounded | X origin (mapped to index 0) |
origin_y |
double |
0.0 |
unbounded | Y origin (mapped to index 0) |
spacing_x |
double |
1.0 |
>0 | Grid spacing in X direction |
spacing_y |
double |
1.0 |
>0 | Grid spacing in Y direction |
Radial Mode Parameters:
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
coord_center_x |
double |
0.0 |
unbounded | Radial center X position |
coord_center_y |
double |
0.0 |
unbounded | Radial center Y position |
coord_velocity_x |
double |
0.0 |
unbounded | Radial center X velocity (units/s) |
coord_velocity_y |
double |
0.0 |
unbounded | Radial center Y velocity (units/s) |
Boundary & Initial Conditions
- 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_xandspacing_y
Stability & Convergence
- Unconditionally stable (pure assignment operation)
- Time-varying radial coordinates depend on simulation time; ensure
time_offsetaligns with your integration scheme - No temporal accumulation issues when
accumulate = false
Performance Notes
- 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
Examples
-- Linear index ramp normalized to [0, 1]
ooc.add_coordinate_operator(ctx, field, {
mode = "index",
normalize = "unit"
})
-- Signed index ramp [-1, 1]
ooc.add_coordinate_operator(ctx, field, {
mode = "index",
normalize = "signed",
gain = 2.0,
bias = 0.5
})
-- X-axis coordinate with custom spacing
ooc.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.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.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.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.add_coordinate_operator(ctx, field, {
mode = "coord",
coord_mode = "separable",
coord_combine = "multiply",
normalize = "signed"
})
Phase Rotate 🔄
add_phase_rotate_operator(ctx, field, opts)
Apply a uniform phase rotation to all field samples. Complex fields use the full complex rotation, while real fields use the projected cosine factor. This makes the operator useful for frequency shifting, demodulation, and rotating reference frames.
Method Signature
add_phase_rotate_operator(ctx, field, [options]) -> operator
Returns: Operator handle (userdata)
Mathematical Formulation
For each complex sample :
where is the phase_rate in radians per second.
Equivalent polar form:
The magnitude is preserved; only the phase advances by per timestep.
For real-valued fields or real-constrained spectra, the operator applies the projected real multiplier
instead of the full complex exponential.
Cartesian implementation:
Parameters
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
phase_rate |
double |
0.0 |
unbounded | Angular rotation rate in rad/s (required) |
Boundary & Initial Conditions
- Operates elementwise; no boundary handling required
- Complex fields rotate in the complex plane; real fields receive the projected cosine factor
- Initial phase determines starting point; rotation is relative
Stability & Convergence
- Complex fields: Rotation is unitary and magnitude-preserving
- Real fields: The projected cosine factor can attenuate or sign-flip samples, so the operation is no longer norm-preserving
- Phase accumulates linearly with time:
- For very large , consider reducing timestep to avoid phase aliasing
Performance Notes
- Requires one sin/cos evaluation per timestep (not per element)
- Complex fields use the full 2x2 rotation; real fields use only the cosine projection
- Very efficient; rotation matrix is computed once per step
- No memory allocation beyond the field itself
Examples
-- Rotate at 1 Hz (2π rad/s)
ooc.add_phase_rotate_operator(ctx, carrier, {
phase_rate = 2 * math.pi
})
-- Negative rotation (clockwise in complex plane)
ooc.add_phase_rotate_operator(ctx, signal, {
phase_rate = -1.0
})
-- Demodulation: shift carrier frequency to baseband
local carrier_freq = 440.0 -- Hz
ooc.add_phase_rotate_operator(ctx, modulated_signal, {
phase_rate = -2 * math.pi * carrier_freq
})
-- Slow phase drift for visualization
ooc.add_phase_rotate_operator(ctx, wave, {
phase_rate = 0.1
})
-- Rotating reference frame for oscillator analysis
local omega_0 = 100.0 -- natural frequency
ooc.add_phase_rotate_operator(ctx, oscillator_state, {
phase_rate = -omega_0 -- move to co-rotating frame
})
Copy 📋
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.
Method Signature
add_copy_operator(ctx, input, output, [options]) -> operator
Returns: Operator handle (userdata)
Mathematical Formulation
For each sample index :
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
accumulate |
boolean |
false |
Add to output instead of overwriting |
scale_by_dt |
boolean |
true |
Scale accumulated writes by dt |
Boundary & Initial Conditions
- 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)
Stability & Convergence
- Pure copy is unconditionally stable
- When accumulating, ensure time integration is handled correctly to avoid unbounded growth
Performance Notes
- 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
Examples
-- Simple field copy
ooc.add_copy_operator(ctx, source, destination)
-- Accumulate input into running sum (scaled by dt)
ooc.add_copy_operator(ctx, flux, integral, {
accumulate = true,
scale_by_dt = true
})
-- Accumulate without dt scaling (raw summation)
ooc.add_copy_operator(ctx, sample, buffer, {
accumulate = true,
scale_by_dt = false
})
-- Double-buffering pattern
local u_prev = ooc.add_field(ctx, {N}, { fill = 0.0 })
ooc.add_copy_operator(ctx, u, u_prev) -- snapshot before update
-- ... operators that modify u ...
Scale ⚖️
add_scale_operator(ctx, input, output, opts)
Scale input field samples by a constant factor. Essential for applying gain, normalization, or unit conversion.
Method Signature
add_scale_operator(ctx, input, output, [options]) -> operator
Returns: Operator handle (userdata)
Mathematical Formulation
For each sample index :
where is the scale parameter.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
scale |
double |
1.0 |
Multiplicative scale factor |
accumulate |
boolean |
false |
Add to output instead of overwriting |
scale_by_dt |
boolean |
true |
Scale accumulated writes by dt |
Boundary & Initial Conditions
- No boundary handling required; operates elementwise
- Input and output fields must have compatible shapes
- Complex fields: scale applies to both real and imaginary parts
Stability & Convergence
- Unconditionally stable for any finite scale value
scale > 1.0amplifies;scale < 1.0attenuatesscale = 0.0zeros the output (preferadd_zero_field_operatorfor clarity)- Negative scales invert the sign of the field
Performance Notes
- Single multiplication per element; highly efficient
- Can operate in-place when
input == output - Consider fusing with other operators when possible to reduce memory bandwidth
Examples
-- Apply gain of 0.5
ooc.add_scale_operator(ctx, signal, attenuated, {
scale = 0.5
})
-- Invert field sign
ooc.add_scale_operator(ctx, u, u_neg, {
scale = -1.0
})
-- In-place scaling
ooc.add_scale_operator(ctx, field, field, {
scale = 0.99 -- gentle decay
})
-- Accumulate scaled values (e.g., for weighted averaging)
ooc.add_scale_operator(ctx, sample, weighted_sum, {
scale = weight,
accumulate = true
})
-- Diffusion coefficient application
local D = 0.01
ooc.add_laplacian_operator(ctx, u, lap_u)
ooc.add_scale_operator(ctx, lap_u, du_dt, { scale = D })
Zero Field 🗑️
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.
Method Signature
add_zero_field_operator(ctx, field) -> operator
Returns: Operator handle (userdata)
Note: This operator takes no options table—just the context and field.
Mathematical Formulation
For each sample index :
For complex fields, both real and imaginary components are set to zero.
Parameters
None. The operator simply zeros the specified field.
Boundary & Initial Conditions
- No boundary handling required; all elements are set to zero
- Operates in-place on the target field
Stability & Convergence
- Unconditionally stable; always produces zeros
- Does not depend on timestep or field state
Performance Notes
- 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)
Examples
-- Zero a field at each step (useful for accumulators)
local accumulator = ooc.add_field(ctx, {N}, { fill = 0.0 })
ooc.add_zero_field_operator(ctx, accumulator)
-- ... operators that accumulate into the field ...
-- Reset state before stimulus injection
ooc.add_zero_field_operator(ctx, state)
ooc.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.add_zero_field_operator(ctx, buffer_b)
ooc.add_copy_operator(ctx, buffer_a, buffer_b, { accumulate = true })