Layer 8 — YAML circuit parser¶
The full v2 stack (CircuitBuilder + power-device helpers +
Python bindings) supported coding in C++ or Python. V8 closes
the loop by adding a YAML loader: engineers describe a
circuit and simulation options in a config file, the loader
returns a populated CircuitBuilder + SimulationOptions
ready to feed into PwlStateSpaceCache and run_transient.
# my_buck.yaml
circuit:
devices:
- type: voltage_source
name: Vin
from: vin
to: gnd
V: 24.0
- type: mosfet_with_body_diode
name: Q1
drain: vin
source: sw
- type: inductor
name: L1
from: sw
to: vout
L: 100e-6
- type: capacitor
name: Cout
from: vout
to: gnd
C: 47e-6
- type: resistor
name: R_L
from: vout
to: gnd
R: 5.0
simulation:
t_start: 0.0
t_end: 5e-3
dt: 1e-7
import pulsim as p
loaded = p.load_yaml_file("my_buck.yaml")
cache = p.PwlStateSpaceCache(loaded.builder.graph,
loaded.builder.pool)
cache.build(loaded.options.dt)
result = p.run_transient(
cache, loaded.builder.graph, loaded.builder.pool,
loaded.options,
switch_fn=lambda t: p.SwitchStateMask(0),
)
print(result.states[-1])
Supported device types¶
The YAML loader supports every Layer 2 V2 device. Each
device entry's type: field selects the schema:
type |
Required fields | Optional |
|---|---|---|
voltage_source |
from, to, V |
— |
resistor |
from, to, R (Ω) |
— |
capacitor |
from, to, C (F) |
— |
inductor |
from, to, L (H) |
— |
diode |
anode, cathode, g_on, g_off |
V_th (default 0) |
nonlinear_diode |
anode, cathode |
V_F0 (0.7), R_d (0.01), G_off (1e-9), kappa (20) |
switch |
from, to, g_on, g_off |
— |
mosfet |
drain, source |
R_on (1mΩ), R_off (1GΩ) |
mosfet_with_body_diode |
drain, source |
R_on (1mΩ), R_off (1GΩ), V_F (0.7), g_on_diode (1k), g_off_diode (1n) |
igbt |
collector, emitter |
R_on (10mΩ), R_off (1GΩ) |
transformer |
p_from, p_to, s_from, s_to, L_p, L_s |
k (1.0) |
name: is optional on every device but recommended for
clearer error messages.
"gnd" / "GND" / "0" are aliases for the ground node
(same as Layer 6's CircuitBuilder).
simulation: block¶
All fields optional; defaults match SimulationOptions{}:
simulation:
t_start: 0.0
t_end: 1e-3
dt: 1e-7
enable_newton_line_search: false
enable_newton_lm: false
enable_substep_state_correction: false
max_event_iterations: 16
max_newton_iterations: 50
tol_newton_dx: 1e-9
tol_newton_res: 1e-9
API¶
C++¶
#include "pulsim/yaml/loader.hpp"
auto loaded = pulsim::yaml::load_file("my_circuit.yaml");
// loaded.builder, loaded.options
Python¶
import pulsim as p
loaded = p.load_yaml_file("path/to/circuit.yaml")
# loaded.builder, loaded.options
# Or from an in-memory string:
loaded = p.load_yaml_string("""
circuit:
devices:
- type: resistor
name: R1
from: a
to: b
R: 100.0
""")
Error handling¶
The loader throws std::runtime_error (in C++) /
RuntimeError (in Python) on:
- Missing required fields per device (e.g. a resistor
without
R:). - Unknown
type:value. - Malformed YAML (re-wrapped from yaml-cpp's parse errors).
- Missing top-level
circuit:orcircuit.devices:.
Errors mention the device's name: if provided, otherwise
the device's index in the YAML's devices: list, plus the
missing field name:
yaml::load: device 'R_load' (resistor) is missing required
field 'R'
Sample YAMLs¶
Three working examples ship in examples/:
half_wave_rectifier.yaml— V_sine source + diode + R_L.buck.yaml— synchronous buck with high-side MOSFET + body diode, low-side freewheeling diode, LC output filter.flyback.yaml— isolated flyback with a 2-winding transformer (k=0.95), MOSFET, rectifier diode, output cap.
These are starting points — engineers iterate by editing the YAML, no recompile / reimport needed.
What V0 deliberately does NOT do¶
- Parameter expressions / formulae (e.g.
R: ${R_load * 2}). V0 takes literal numbers only. V1 may add expression evaluation. include:directive for composing YAMLs. V1.- Device-by-name lookup at runtime. Names are diagnostic-only in V0.
- Schema versioning (
version:field). Forward compatibility lives in "extra unknown fields are silently ignored" — V1 may add explicit version negotiation. - Symbolic units (
R: "10 kΩ"). Only raw numbers in V0. SPICE-style suffixes (k, m, u, n, p) are V1. - Schedule (switch_fn / b_extra_fn) definition inside
the YAML. The user supplies these from C++ or Python.
V1 may add
schedule:blocks (e.g. PWM frequency, duty cycle) that generate switch_fn callbacks.
Test coverage¶
C++ — 17 cases in
core/tests/v2/yaml/test_yaml_loader.cpp:
- Round-trip per device type (10 cases).
simulation:block parsing.- Missing-field errors (with device name in message).
- Unknown-type errors.
- Malformed YAML errors.
- Missing top-level
circuit:throws. - Direct vs YAML equivalence on half-wave rectifier.
- DC solve roundtrip via YAML.
Python — 4 cases in
python/tests/v2/test_v2_python_bindings.py:
load_yaml_stringbasic.load_yaml_stringmissing-field error.load_yaml_filereads disk + parses the half-wave example.load_yaml_filereads buck.yaml + verifies branch count.
Files¶
- NEW
core/include/pulsim/yaml/loader.hpp - NEW
core/tests/v2/yaml/test_main.cpp - NEW
core/tests/v2/yaml/test_yaml_loader.cpp - MODIFIED
core/CMakeLists.txt(yaml test target) - MODIFIED
python/bindings_v2_kernel.cpp - MODIFIED
python/pulsim/v2.py(re-export YAML symbols) - MODIFIED
python/tests/v2/test_v2_python_bindings.py(+ 4 cases) - NEW
examples/half_wave_rectifier.yaml - NEW
examples/buck.yaml - NEW
examples/flyback.yaml - NEW
openspec/changes/pulsim-v2-yaml-parser/