Operator Calculus, Co.
Browse Studio documentation
On this page

Quick Reference ๐Ÿ“Œ

  • add_field(ctx, shape[, options]) allocates a field and returns a Field handle (userdata) pinned to the context lifetime.
  • Shape: Lua array of positive integers, one per dimension. Axis 0 is the slowest (outermost loop); axis rank-1 is the fastest (innermost).
  • Element type: "double" (real, 8 bytes/element) or "complex_double" (interleaved re/im, 16 bytes/element).
  • Seeding: fill sets every element at allocation time. initializer (Lua function) runs per-element after fill and supports 1D and 2D fields.
  • Representation metadata: domain, value_kind, or representation = { ... } tag the field as physical vs spectral and real vs complex-valued without changing the underlying storage type.
  • Coordinates: origin and spacing define the physical coordinate grid passed to the initializer. They are consumed during initialization only and are not stored on the field.

Method Signature ๐Ÿงพ

add_field(ctx, shape[, options]) -> field

Returns: Field handle (userdata) โ€” pass it to operators, step functions, and field queries.

Options

Option Type Default Description
type string "double" "complex_double" stores interleaved {re, im} pairs. Unrecognized strings fall back to "double".
fill double or {re, im} 0 Seeds every element before the initializer runs. For complex fields, supply a {re, im} table; a bare number is ignored for complex storage.
initializer function none Per-element Lua callback called after fill. Supported for 1D and 2D fields only. See signatures below.
origin double 0.0 Physical coordinate of element index 0 along every axis (isotropic โ€” same for all axes).
spacing double 1.0 Step size between adjacent elements in physical coordinates (isotropic).
domain string or enum derived Representation domain. Canonical values are "physical" and "spectral"; "spatial" and "frequency" are accepted aliases.
value_kind string or enum derived Scalar interpretation metadata. Canonical values are "real", "complex", and "complex_real_constraint"; aliases like "scalar", "hermitian", and "conjugate_symmetric" are accepted.
representation table none Nested { domain = ..., value_kind = ... } override. Parsed after the top-level domain / value_kind keys and takes precedence when both are present.

Note

Representation metadata does not change storage allocation. A real field still uses "double" storage and a complex field still uses "complex_double" storage. By default, "double" fields start as physical + real, while "complex_double" fields start as physical + complex.

Caution

Not every representation combination is valid. Spectral fields require complex storage, and complex_real_constraint is only valid in the spectral domain. The hybrid domain exists in core enums but is not currently accepted by add_field.


Initializer Signatures ๐Ÿ”ข

The initializer function is called once per element. Physical coordinates are derived from origin and spacing. Return a number for real fields or a {re, im} table for complex fields.

1D Fields โ€” initializer(i, x)

Argument Description
i 0-based element index (0 to shape[1] - 1)
x Physical coordinate: origin + i * spacing
-- Complex travelling wave
local wave = ooc.add_field(ctx, {1024}, {
    type    = "complex_double",
    origin  = -math.pi,
    spacing = 2 * math.pi / 1024,
    initializer = function(i, x)
        return { math.cos(3 * x), math.sin(3 * x) }
    end
})

2D Fields โ€” initializer(ix, iy, x, y)

Argument Description
ix 0-based index along axis 0 (outer / row dimension, 0 to shape[1] - 1)
iy 0-based index along axis 1 (inner / column dimension, 0 to shape[2] - 1)
x Physical coordinate along axis 0: origin + ix * spacing
y Physical coordinate along axis 1: origin + iy * spacing
-- Gaussian blob centred at (0, 0)
local pattern = ooc.add_field(ctx, {256, 256}, {
    origin  = -1.0,
    spacing = 2.0 / 256,
    initializer = function(ix, iy, x, y)
        return math.exp(-8 * (x * x + y * y))
    end
})

Caution

Initializers for rank โ‰ฅ 3 fields raise a Lua error. Use fill at creation time and then field:set_values() (or a stimulus operator) to write higher-dimensional data.

The iteration order for 2D fields is: outer loop over ix (axis 0), inner loop over iy (axis 1), matching the row-major element layout returned by field:values().


Examples ๐ŸŽ‰

Real 2D Grid โ€” Uniform Fill

local ctx = ooc.create()

local density = ooc.add_field(ctx, {256, 256}, { fill = 0.0 })
ooc.log("rank=%d elements=%d", density:rank(), #density:values())

Complex 1D โ€” Phase-Encoded Waveform

local wave = ooc.add_field(ctx, {1024}, {
    type    = "complex_double",
    origin  = -math.pi,
    spacing = 2 * math.pi / 1024,
    initializer = function(i, x)
        return { math.cos(3 * x), math.sin(3 * x) }
    end
})

local v = wave:values()
ooc.log("wave[0] = %.3f + %.3fi", v[1][1], v[1][2])

Real 2D โ€” Radial Gaussian

local blob = ooc.add_field(ctx, {256, 256}, {
    origin  = -1.0,
    spacing = 2.0 / 256,
    initializer = function(ix, iy, x, y)
        return math.exp(-8 * (x * x + y * y))
    end
})

Complex 2D โ€” Spiral Phase Ramp

local phase = ooc.add_field(ctx, {512, 512}, {
    type    = "complex_double",
    origin  = -math.pi,
    spacing = 2 * math.pi / 512,
    initializer = function(ix, iy, x, y)
        local theta = math.atan(y, x)
        return { math.cos(theta), math.sin(theta) }
    end
})

Spectral Metadata on Complex Storage

Use representation tags when a field should be treated as a spectral buffer or as Hermitian-constrained complex data:

local spectrum = ooc.add_field(ctx, {256}, {
    type = "complex_double",
    representation = {
        domain = "spectral",
        value_kind = "complex_real_constraint",
    },
    fill = {0.0, 0.0}
})

ooc.log("domain=%s value_kind=%s", spectrum:domain(), spectrum:value_kind())

Seeding Without Initializer โ€” fill + set_values

For rank โ‰ฅ 3 or when the seed data comes from outside Lua, allocate with fill and then write via field:set_values():

local vol = ooc.add_field(ctx, {64, 64, 64}, { fill = 0.0 })

-- Build a flat table of 64^3 values, then write:
local data = {}
for i = 1, 64 * 64 * 64 do data[i] = math.random() end
vol:set_values(data)

Best Practices ๐Ÿ’ก

  1. Choose type up front โ€” element size is fixed at allocation and determines byte layout for all operators attached to that field.
  2. Prefer fill for constant seeds; use initializer for per-element geometry or waveform shapes (1D and 2D only).
  3. For large fields or complex seeding logic, add_stimulus_operator (e.g. stimulus_rd_seed) is more efficient than a Lua initializer.
  4. Capture the returned Field handle โ€” it is needed to attach operators and to call :rank(), :shape(), :values().
  5. Fields are stored contiguously in row-major order; shape[1] is the outermost dimension and shape[rank] is the innermost (fastest-varying).