JSON Graph Schema
FluxGraph JSON files define signal processing graphs using a simple, validated structure. This document specifies the schema for graph files loaded with load_json_file() and load_json_string().
JSON Structure
A graph file has four top-level arrays (all optional):
{
"signals": [
/* Signal unit contracts */
],
"models": [
/* Physics models */
],
"edges": [
/* Signal edges with transforms */
],
"rules": [
/* Conditional actions */
]
}
Signals
Signals declare explicit unit contracts used by dimensional validation.
{
"path": "chamber.temp",
"unit": "degC"
}
Fields:
path(string, required) - Signal pathunit(string, required) - Unit symbol (dimensionless,W,degC, etc.)
Models
Models represent physical systems with internal state and differential equations.
Model Object
{
"id": "unique_identifier",
"type": "model_type",
"params": {
"param_name": value
}
}
Fields:
id(string, required) - Unique model identifiertype(string, required) - Model type (built-in:"thermal_mass","thermal_rc2","first_order_process","second_order_process")params(object, required) - Model-specific parameters
ThermalMass Model
Lumped thermal capacitance with heat transfer: C * dT/dt = P - h * (T - T_ambient)
Parameters:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| temp_signal | string | - | Output signal path for temperature |
| power_signal | string | W | Input signal path for heating power |
| ambient_signal | string | degC | Input signal path for ambient temperature |
| thermal_mass | number | J/K | Heat capacity (must be > 0) |
| heat_transfer_coeff | number | W/K | Heat transfer coefficient (must be > 0) |
| initial_temp | number | degC | Initial temperature |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| temp_signal_a | string | - | Output signal path for node A temperature |
| temp_signal_b | string | - | Output signal path for node B temperature |
| power_signal | string | W | Input signal path for heating power (applied to node A) |
| ambient_signal | string | degC | Input signal path for ambient temperature |
| thermal_mass_a | number | J/K | Heat capacity of node A (must be > 0) |
| thermal_mass_b | number | J/K | Heat capacity of node B (must be > 0) |
| heat_transfer_coeff_a | number | W/K | Ambient coupling of node A (must be > 0) |
| heat_transfer_coeff_b | number | W/K | Ambient coupling of node B (must be > 0) |
| coupling_coeff | number | W/K | Conductance between nodes (must be >= 0) |
| initial_temp_a | number | degC | Initial temperature of node A |
| initial_temp_b | number | degC | Initial temperature of node B |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| output_signal | string | dimensionless | Output signal path |
| input_signal | string | dimensionless | Input signal path |
| gain | number | dimensionless | Static gain (finite) |
| tau_s | number | s | Time constant (must be > 0) |
| initial_output | number | dimensionless | Initial output value |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| output_signal | string | dimensionless | Output signal path |
| input_signal | string | dimensionless | Input signal path |
| gain | number | dimensionless | Static gain (finite) |
| zeta | number | dimensionless | Damping ratio (must be >= 0) |
| omega_n_rad_s | number | 1/s | Natural frequency (must be > 0) |
| initial_output | number | dimensionless | Initial output value |
| initial_output_rate | number | 1/s | Initial output rate |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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"
}
}
MassSpringDamper Model
Translational single-degree-of-freedom mass-spring-damper:
m*x'' + c*x' + k*x = F
Parameters:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| position_signal | string | m | Output position signal path |
| velocity_signal | string | m/s | Output velocity signal path |
| force_signal | string | N | Input force signal path |
| mass | number | kg | Mass (must be > 0) |
| damping_coeff | number | N*s/m | Damping coefficient (must be >= 0) |
| spring_constant | number | N/m | Spring constant (must be >= 0) |
| initial_position | number | m | Initial position |
| initial_velocity | number | m/s | Initial velocity |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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:
| Parameter | Type | Units | Description |
|ββββ|ββ|ββ-|ββββ-|
| speed_signal | string | rad/s | Output speed signal path |
| current_signal | string | A | Output current signal path |
| torque_signal | string | Nm | Output electromagnetic torque signal path |
| voltage_signal | string | V | Input voltage signal path |
| load_torque_signal | string | Nm | Input load torque signal path |
| resistance_ohm | number | Ohm | Armature resistance (must be > 0) |
| inductance_h | number | H | Armature inductance (must be > 0) |
| torque_constant | number | Nm/A | Torque constant (must be > 0) |
| back_emf_constant | number | Vs/rad | Back-EMF constant (must be > 0) |
| inertia | number | kgm^2 | Rotor inertia (must be > 0) |
| viscous_friction | number | Nm*s/rad | Viscous friction (must be >= 0) |
| initial_current | number | A | Initial armature current |
| initial_speed | number | rad/s | Initial angular speed |
| integration_method | string | - | Optional: "forward_euler" (default) or "rk4" |
Example:
{
"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
{
"source": "source.signal.path",
"target": "target.signal.path",
"transform": {
"type": "transform_type",
"params": {
/* transform parameters */
}
}
}
Fields:
source(string, required) - Source signal pathtarget(string, required) - Target signal pathtransform(object, required) - Transform specification
Transforms
All 9 transform types with their parameters:
1. Linear Transform
Type: "linear"
Scale and offset: y = scale * x + offset
Parameters:
| Parameter | Type | Required | Default | Description |
|ββββ|ββ|βββ-|βββ|ββββ-|
| scale | number | yes | - | Multiplicative gain |
| offset | number | yes | - | Additive offset |
| clamp_min | number | no | -infinity | Minimum output value |
| clamp_max | number | no | +infinity | Maximum output value |
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| tau_s | number | Time constant in seconds (must be > 0) |
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| delay_sec | number | Delay duration in seconds (must be >= 0) |
Example:
{
"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:
| Parameter | Type | Required | Description |
|ββββ|ββ|βββ-|ββββ-|
| amplitude | number | yes | Standard deviation of noise |
| seed | integer | no | Random seed for repeatability |
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| min | number | Minimum output value |
| max | number | Maximum output value|
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| threshold | number | Sensitivity threshold (must be >= 0) |
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| max_rate_per_sec | number | Maximum rate in units/second (must be > 0) |
Example:
{
"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:
| Parameter | Type | Description |
|ββββ|ββ|ββββ-|
| window_size | integer | Number of samples to average (must be >= 1) |
Example:
{
"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:
| Parameter | Type | Required | Description |
|ββββ|ββ|βββ-|ββββ-|
| to_unit | string | yes | Target unit symbol |
| from_unit | string | no | Optional source-unit assertion |
Example:
{
"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
{
"id": "unique_identifier",
"condition": "signal_path > value",
"actions": [
{
"device": "device_id",
"function": "function_name",
"args": {
/* function arguments */
}
}
],
"on_error": "log_and_continue"
}
Fields:
id(string, required) - Unique rule identifiercondition(string, required) - Simple comparison (basic parser in v1.0)actions(array, required) - Array of action objectson_error(string, optional) - Error handling (βlog_and_continueβ only in v1.0)
Example:
{
"id": "heater_on",
"condition": "chamber.temp < 20.0",
"actions": [
{
"device": "heater",
"function": "set_power",
"args": {
"power": 500.0
}
}
]
}
Complete Example
{
"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
}
}
],
"edges": [
{
"source": "heater.output",
"target": "chamber.power",
"transform": {
"type": "saturation",
"params": {
"min": 0.0,
"max": 1000.0
}
}
},
{
"source": "chamber.temp",
"target": "sensor.reading",
"transform": {
"type": "first_order_lag",
"params": {
"tau_s": 0.5
}
}
},
{
"source": "sensor.reading",
"target": "display.temp",
"transform": {
"type": "noise",
"params": {
"amplitude": 0.1,
"seed": 42
}
}
}
],
"rules": [
{
"id": "low_temp_alarm",
"condition": "chamber.temp < 18.0",
"actions": [
{
"device": "heater",
"function": "set_power",
"args": {
"power": 1000.0
}
}
]
}
]
}
Error Messages
The JSON loader provides detailed error messages with JSON pointer paths:
JSON parse error at /edges/2/transform: Missing required field 'type'
JSON parse error at /models/0/params/thermal_mass: Unsupported type for Variant
JSON parse error in file graph.json: [nlohmann::json parse error]
JSON Schema Validation
For tools that support JSON Schema, hereβs a minimal schema excerpt:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"signals": {
"type": "array",
"items": { "$ref": "#/definitions/signal" }
},
"models": {
"type": "array",
"items": { "$ref": "#/definitions/model" }
},
"edges": {
"type": "array",
"items": { "$ref": "#/definitions/edge" }
},
"rules": {
"type": "array",
"items": { "$ref": "#/definitions/rule" }
}
}
}
Full JSON Schema definition: See schema/fluxgraph-v1.schema.json (planned for v1.1)
Usage
C++ API
#include "fluxgraph/loaders/json_loader.hpp"
// Load from file
auto spec = fluxgraph::loaders::load_json_file("graph.json");
// Load from string
std::string json_content = R"({ "edges": [...] })";
auto spec2 = fluxgraph::loaders::load_json_string(json_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_JSON_ENABLED=ON at CMake configure time:
cmake -B build -DFLUXGRAPH_JSON_ENABLED=ON
cmake --build build
See Also
- YAML_SCHEMA.md - YAML equivalent (more human-friendly)
- API.md - C++ API reference
- TRANSFORMS.md - Detailed transform documentation
- examples/03_json_graph/ - Working example