Skip to content

YAML Graph Schema

FluxGraph YAML files define signal processing graphs with a human-friendly syntax. This document specifies the schema for graph files loaded with load_yaml_file() and load_yaml_string().

YAML Structure

A graph file has four top-level sequences (all optional):

yaml
signals:
  - path: signal.path
    unit: unit_symbol

models:
  - id: unique_identifier
    type: model_type
    params:
      param_name: value

edges:
  - source: source.signal.path
    target: target.signal.path
    transform:
      type: transform_type
      params:
        param_name: value

rules:
  - id: rule_identifier
    condition: "signal_path > value"
    actions:
      - device: device_id
        function: function_name
        args:
          arg_name: value

Signals

Signals declare explicit unit contracts used by dimensional validation.

yaml
signals:
  - path: chamber.temp
    unit: degC

Fields:

  • path (string, required) - Signal path
  • unit (string, required) - Unit symbol (dimensionless, W, degC, etc.)

Models

Models represent physical systems with internal state and differential equations.

Model Object

yaml
- id: unique_identifier # Unique model identifier
  type: model_type # Model type (thermal_mass, thermal_rc2, first_order_process, second_order_process, state_space_siso_discrete, mass_spring_damper, dc_motor)
  params: # Model-specific parameters
    param_name: value

Parameter Value Types (Models and Transforms)

Model/transform params use structured values:

  1. scalar: number, integer, boolean, string
  2. sequence: YAML list of structured values
  3. mapping: YAML map with structured values

Loader resource limits are enforced to reject abusive payloads:

  1. max nesting depth: 32
  2. max total nodes: 250000
  3. max object members: 4096
  4. max array elements: 65536
  5. max string bytes: 1048576

ThermalMass Model

Lumped thermal capacitance with heat transfer: C * dT/dt = P - h * (T - T_ambient)

Parameters:

ParameterTypeUnitsDescription
temp_signalstring-Output signal path for temperature
power_signalstringWInput signal path for heating power
ambient_signalstringdegCInput signal path for ambient temperature
thermal_massnumberJ/KHeat capacity (must be > 0)
heat_transfer_coeffnumberW/KHeat transfer coefficient (must be > 0)
initial_tempnumberdegCInitial temperature
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: chamber
    type: thermal_mass
    params:
      temp_signal: chamber.temp
      power_signal: chamber.power
      ambient_signal: ambient.temp
      thermal_mass: 1000.0
      heat_transfer_coeff: 10.0
      initial_temp: 25.0

Stability: Forward Euler integration requires dt < 2 * thermal_mass / heat_transfer_coeff

ThermalRc2 Model

Two-node thermal RC network with ambient coupling and inter-node conductance.

Parameters:

ParameterTypeUnitsDescription
temp_signal_astring-Output signal path for node A temperature
temp_signal_bstring-Output signal path for node B temperature
power_signalstringWInput signal path for heating power (applied to node A)
ambient_signalstringdegCInput signal path for ambient temperature
thermal_mass_anumberJ/KHeat capacity of node A (must be > 0)
thermal_mass_bnumberJ/KHeat capacity of node B (must be > 0)
heat_transfer_coeff_anumberW/KAmbient coupling of node A (must be > 0)
heat_transfer_coeff_bnumberW/KAmbient coupling of node B (must be > 0)
coupling_coeffnumberW/KConductance between nodes (must be >= 0)
initial_temp_anumberdegCInitial temperature of node A
initial_temp_bnumberdegCInitial temperature of node B
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: chamber_rc
    type: thermal_rc2
    params:
      temp_signal_a: chamber.shell_temp
      temp_signal_b: chamber.core_temp
      power_signal: chamber.heater_power
      ambient_signal: ambient.temp
      thermal_mass_a: 1000.0
      thermal_mass_b: 2000.0
      heat_transfer_coeff_a: 10.0
      heat_transfer_coeff_b: 8.0
      coupling_coeff: 6.0
      initial_temp_a: 25.0
      initial_temp_b: 25.0
      integration_method: rk4

FirstOrderProcess Model

First-order process primitive: dy/dt = (gain * u - y) / tau.

Parameters:

ParameterTypeUnitsDescription
output_signalstringdimensionlessOutput signal path
input_signalstringdimensionlessInput signal path
gainnumberdimensionlessStatic gain (finite)
tau_snumbersTime constant (must be > 0)
initial_outputnumberdimensionlessInitial output value
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: pt1
    type: first_order_process
    params:
      output_signal: pt1.y
      input_signal: pt1.u
      gain: 2.0
      tau_s: 1.0
      initial_output: 0.0

SecondOrderProcess Model

Second-order process primitive: y'' + 2*zeta*omega_n*y' + omega_n^2*y = omega_n^2 * gain * u

Parameters:

ParameterTypeUnitsDescription
output_signalstringdimensionlessOutput signal path
input_signalstringdimensionlessInput signal path
gainnumberdimensionlessStatic gain (finite)
zetanumberdimensionlessDamping ratio (must be >= 0)
omega_n_rad_snumber1/sNatural frequency (must be > 0)
initial_outputnumberdimensionlessInitial output value
initial_output_ratenumber1/sInitial output rate
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: pt2
    type: second_order_process
    params:
      output_signal: pt2.y
      input_signal: pt2.u
      gain: 2.0
      zeta: 0.7
      omega_n_rad_s: 4.0
      initial_output: 0.0
      initial_output_rate: 0.0
      integration_method: rk4

StateSpaceSisoDiscrete Model

Discrete-time SISO state-space model: x[k+1] = A_d x[k] + B_d u[k], y[k] = C x[k] + D u[k]

Parameters:

ParameterTypeUnitsDescription
output_signalstringuser-definedOutput signal path
input_signalstringuser-definedInput signal path
A_dsequence of sequences-Square matrix (n x n)
B_dsequence-Input vector (n)
Csequence-Output vector (n)
Dnumber-Feedthrough scalar
x0sequence-Initial state vector (n)

Validation notes:

  1. A_d must be non-empty, rectangular, and square.
  2. B_d, C, and x0 lengths must match A_d dimension.
  3. All numeric values must be finite.
  4. In strict dimensional mode, input_signal and output_signal must have declared signal contracts.
  5. Internal state-unit algebra is currently out of scope.

Example:

yaml
models:
  - id: ss
    type: state_space_siso_discrete
    params:
      output_signal: ss.y
      input_signal: ss.u
      A_d:
        - [0.9, 0.1]
        - [0.0, 0.95]
      B_d: [0.1, 0.05]
      C: [1.0, 0.0]
      D: 0.0
      x0: [0.0, 0.0]

MassSpringDamper Model

Translational single-degree-of-freedom mass-spring-damper: m*x'' + c*x' + k*x = F

Parameters:

ParameterTypeUnitsDescription
position_signalstringmOutput position signal path
velocity_signalstringm/sOutput velocity signal path
force_signalstringNInput force signal path
massnumberkgMass (must be > 0)
damping_coeffnumberN*s/mDamping coefficient (must be >= 0)
spring_constantnumberN/mSpring constant (must be >= 0)
initial_positionnumbermInitial position
initial_velocitynumberm/sInitial velocity
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: msd
    type: mass_spring_damper
    params:
      position_signal: msd.x
      velocity_signal: msd.v
      force_signal: msd.F
      mass: 1.0
      damping_coeff: 2.0
      spring_constant: 20.0
      initial_position: 0.0
      initial_velocity: 0.0
      integration_method: rk4

DcMotor Model

Armature-controlled DC motor with electrical inductance and viscous friction.

Parameters:

ParameterTypeUnitsDescription
speed_signalstringrad/sOutput speed signal path
current_signalstringAOutput current signal path
torque_signalstringN*mOutput electromagnetic torque signal path
voltage_signalstringVInput voltage signal path
load_torque_signalstringN*mInput load torque signal path
resistance_ohmnumberOhmArmature resistance (must be > 0)
inductance_hnumberHArmature inductance (must be > 0)
torque_constantnumberN*m/ATorque constant (must be > 0)
back_emf_constantnumberV*s/radBack-EMF constant (must be > 0)
inertianumberkg*m^2Rotor inertia (must be > 0)
viscous_frictionnumberNms/radViscous friction (must be >= 0)
initial_currentnumberAInitial armature current
initial_speednumberrad/sInitial angular speed
integration_methodstring-Optional: forward_euler (default) or rk4

Example:

yaml
models:
  - id: motor
    type: dc_motor
    params:
      speed_signal: motor.omega
      current_signal: motor.i
      torque_signal: motor.tau
      voltage_signal: motor.V
      load_torque_signal: motor.load
      resistance_ohm: 2.0
      inductance_h: 0.5
      torque_constant: 0.1
      back_emf_constant: 0.1
      inertia: 0.02
      viscous_friction: 0.2
      initial_current: 0.0
      initial_speed: 0.0
      integration_method: rk4

Edges

Edges connect signals through transforms, defining the dataflow graph.

Edge Object

yaml
- source: source.signal.path # Source signal path
  target: target.signal.path # Target signal path
  transform: # Transform specification
    type: transform_type
    params:
      param_name: value

Transforms

All 9 transform types with their parameters:

1. Linear Transform

Type: linear

Scale and offset: y = scale * x + offset

Parameters:

ParameterTypeRequiredDefaultDescription
scalenumberyes-Multiplicative gain
offsetnumberyes-Additive offset
clamp_minnumberno-infinityMinimum output value
clamp_maxnumberno+infinityMaximum output value

Example:

yaml
edges:
  - source: sensor.raw
    target: sensor.scaled
    transform:
      type: linear
      params:
        scale: 2.5
        offset: -10.0
        clamp_min: 0.0
        clamp_max: 100.0

2. First-Order Lag

Type: first_order_lag

Low-pass filter: tau * dy/dt + y = x

Parameters:

ParameterTypeDescription
tau_snumberTime constant in seconds (must be > 0)

Example:

yaml
edges:
  - source: sensor.noisy
    target: sensor.filtered
    transform:
      type: first_order_lag
      params:
        tau_s: 0.5

Frequency Response: 3dB cutoff at f_c = 1 / (2*pi*tau)

3. Delay Transform

Type: delay

Time-shift signal: y(t) = x(t - delay_sec)

Parameters:

ParameterTypeDescription
delay_secnumberDelay duration in seconds (must be >= 0)

Example:

yaml
edges:
  - source: input.signal
    target: delayed.signal
    transform:
      type: delay
      params:
        delay_sec: 0.1

Memory: Approx delay_sec / dt * 8 bytes (circular buffer)

4. Noise Transform

Type: noise

Add Gaussian white noise: y = x + N(0, amplitude)

Parameters:

ParameterTypeRequiredDescription
amplitudenumberyesStandard deviation of noise
seedintegernoRandom seed for repeatability

Example:

yaml
edges:
  - source: sensor.ideal
    target: sensor.noisy
    transform:
      type: noise
      params:
        amplitude: 0.5
        seed: 42

5. Saturation Transform

Type: saturation

Clamp to bounds: y = clamp(x, min, max)

Parameters:

ParameterTypeDescription
minnumberMinimum output value
maxnumberMaximum output value

Example:

yaml
edges:
  - source: controller.output
    target: actuator.input
    transform:
      type: saturation
      params:
        min: 0.0
        max: 100.0

6. Deadband Transform

Type: deadband

Zero output below threshold: y = (|x| < threshold) ? 0.0 : x

Parameters:

ParameterTypeDescription
thresholdnumberSensitivity threshold (must be >= 0)

Example:

yaml
edges:
  - source: joystick.raw
    target: joystick.gated
    transform:
      type: deadband
      params:
        threshold: 0.05

7. Rate Limiter

Type: rate_limiter

Limit rate of change: |dy/dt| <= max_rate_per_sec

Parameters:

ParameterTypeDescription
max_rate_per_secnumberMaximum rate in units/second (must be > 0)

Example:

yaml
edges:
  - source: setpoint.target
    target: setpoint.ramped
    transform:
      type: rate_limiter
      params:
        max_rate_per_sec: 5.0

Settling Time: Approx delta_V / max_rate_per_sec for step change

8. Moving Average

Type: moving_average

Sliding window average (FIR filter): y = (1/N) * sum(x[n-i]) for i=0 to N-1

Parameters:

ParameterTypeDescription
window_sizeintegerNumber of samples to average (must be >= 1)

Example:

yaml
edges:
  - source: sensor.jittery
    target: sensor.smoothed
    transform:
      type: moving_average
      params:
        window_size: 10

Memory: window_size * 8 bytes per instance

9. Unit Convert

Type: unit_convert

Explicit cross-unit conversion transform. Conversion coefficients are derived from the built-in unit registry.

Parameters:

ParameterTypeRequiredDescription
to_unitstringyesTarget unit symbol
from_unitstringnoOptional source-unit assertion

Example:

yaml
edges:
  - source: sensor.temp_c
    target: sensor.temp_k
    transform:
      type: unit_convert
      params:
        to_unit: K
        from_unit: degC

Rules

Rules trigger device actions when conditions are met.

Rule Object

yaml
- id: unique_identifier # Unique rule identifier
  condition: "signal > value" # Simple comparison
  actions: # Array of action objects
    - device: device_id
      function: function_name
      args:
        arg_name: value
  on_error: log_and_continue # Error handling (optional)

Action args constraint:

  1. command args are scalar-only (double, int64, bool, string)
  2. nested mappings/sequences in args are rejected at load time

Example:

yaml
rules:
  - id: heater_on
    condition: "chamber.temp < 20.0"
    actions:
      - device: heater
        function: set_power
        args:
          power: 500.0

Complete Example

yaml
# Thermal chamber simulation with sensor dynamics

models:
  - id: chamber
    type: thermal_mass
    params:
      temp_signal: chamber.temp
      power_signal: chamber.power
      ambient_signal: ambient.temp
      thermal_mass: 1000.0 # J/K
      heat_transfer_coeff: 10.0 # W/K
      initial_temp: 25.0 # degC

edges:
  # Heater with saturation
  - source: heater.output
    target: chamber.power
    transform:
      type: saturation
      params:
        min: 0.0 # No cooling
        max: 1000.0 # Max 1kW

  # Sensor lag (thermal mass)
  - source: chamber.temp
    target: sensor.reading
    transform:
      type: first_order_lag
      params:
        tau_s: 0.5 # 500ms response

  # Measurement noise
  - source: sensor.reading
    target: display.temp
    transform:
      type: noise
      params:
        amplitude: 0.1 # +/-0.1 degC
        seed: 42 # Repeatable

rules:
  - id: low_temp_alarm
    condition: "chamber.temp < 18.0"
    actions:
      - device: heater
        function: set_power
        args:
          power: 1000.0 # Emergency heat

YAML-Specific Features

Anchors and Aliases

Reuse configurations with &anchor and *alias:

yaml
# Define reusable transform
_noise: &noise_01
  type: noise
  params:
    amplitude: 0.1
    seed: 42

edges:
  - source: sensor1.raw
    target: sensor1.noisy
    transform: *noise_01 # Reuse

  - source: sensor2.raw
    target: sensor2.noisy
    transform: *noise_01 # Same config

Multi-line Strings

Use | or > for long conditions:

yaml
rules:
  - id: complex_condition
    condition: |
      chamber.temp > 50.0 &&
      chamber.temp < 100.0
    actions:
      - device: alarm
        function: trigger

Comments

YAML supports inline and full-line comments:

yaml
models:
  - id: chamber # Main thermal mass
    type: thermal_mass
    params:
      thermal_mass: 1000.0 # Aluminum block
      initial_temp: 25.0 # Room temperature

Compact Collections

Flow style for short lists:

yaml
edges:
  - source: input.x
    target: output.y
    transform: { type: linear, params: { scale: 2.0, offset: 0.0 } }

Error Messages

The YAML loader provides detailed error messages with node locations:

YAML parse error at edges[2].transform: Missing required field 'type'
YAML parse error at rules[0].actions[0].args.payload: Command args must be scalar (double/int64/bool/string)
Parameter parse error at models[0].params.x: nesting depth exceeds limit (32)
YAML parse error in file graph.yaml: [yaml-cpp internal error]

YAML Schema Validation

For tools that support YAML Schema (based on JSON Schema):

yaml
# yaml-language-server: $schema=https://example.com/fluxgraph-v1.schema.json

models:
  - id: chamber
    # ... VSCode will provide autocomplete and validation

Schema file planned for v1.1.

Usage

C++ API

cpp
#include "fluxgraph/loaders/yaml_loader.hpp"

// Load from file
auto spec = fluxgraph::loaders::load_yaml_file("graph.yaml");

// Load from string
std::string yaml_content = R"(
edges:
  - source: a.x
    target: b.y
    transform:
      type: linear
      params:
        scale: 2.0
        offset: 0.0
)";
auto spec2 = fluxgraph::loaders::load_yaml_string(yaml_content);

// Compile and run
fluxgraph::GraphCompiler compiler;
auto program = compiler.compile(spec, signal_ns, func_ns);
engine.load(std::move(program));

Build Configuration

Requires -DFLUXGRAPH_YAML_ENABLED=ON at CMake configure time:

bash
cmake -B build -DFLUXGRAPH_YAML_ENABLED=ON
cmake --build build

JSON vs YAML

Choose based on your use case:

FormatWhen to Use
JSON- Machine-generated graphs
- API payloads
- Strict validation required
- Minimal dependencies
YAML- Hand-written configurations
- Need comments/documentation
- Want anchors/aliases for DRY
- Human-readable priority

Example: Use JSON for programmatically generated test cases, YAML for production configurations with extensive documentation.

See Also